我这里有 Spring Boot Web 应用程序。
升级为以下设置:
当我启动应用程序时,我在控制台上看到这一行:
使用 Spring Boot v3.0.4、Spring v6.0.6 运行
我在使用 Spring Security 配置时遇到问题。
下
application.properties
我有这样的说法:
spring.security.filter.dispatcher-types=request,async,error,forward,include
我正在关注这个 Spring Boot 迁移指南:
接下来是安全配置 Java 文件。
@Configuration
@Order(SecurityProperties.DEFAULT_FILTER_ORDER)
@EnableMethodSecurity(securedEnabled = true, prePostEnabled = true)
@EnableWebSecurity
public class SecConfiguration {
private final HouseUserDetailsService customUserDetailsService;
private final HouseShiftConfiguration dashShiftConfig;
private final DBI dbi;
private final AuditLogService auditLogService;
@Autowired
private CustomSecurityconfiguration customSecurityConfiguration;
@Autowired
public SecConfiguration(HouseUserDetailsService customUserDetailsService, DBI dbi, HouseShiftConfiguration dashShiftConfig, AuditLogService auditLogService) {
this.customUserDetailsService = customUserDetailsService;
this.dbi = dbi;
this.dashShiftConfig = dashShiftConfig;
this.auditLogService = auditLogService;
}
@Bean
public HouseRedirectAuthenticationSuccessHandler houseRedirectAuthenticationSuccessHandler() {
return new HouseRedirectAuthenticationSuccessHandler(dbi);
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfiguration) throws Exception {
return authConfiguration.getAuthenticationManager();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
HttpSessionRequestCache requestCache = new HttpSessionRequestCache();
requestCache.setMatchingRequestParameterName("continue");
CsrfTokenRequestAttributeHandler csrfRequestHandler = new CsrfTokenRequestAttributeHandler();
csrfRequestHandler.setCsrfRequestAttributeName("_csrf");// set the name of the attribute the CsrfToken will be populated on
XorCsrfTokenRequestAttributeHandler xorCsrfRequestHandler = new XorCsrfTokenRequestAttributeHandler();
xorCsrfRequestHandler.setCsrfRequestAttributeName("_csrf");// set the name of the attribute the CsrfToken will be populated on
//csrf white list, when you don't want to use csrf, put your url here.
String[] csrfWhiteList = {
"/emailSender/**"
};
http
.csrf((csrf) -> csrf
.csrfTokenRequestHandler(csrfRequestHandler)
)
.csrf((xorCsrf) -> xorCsrf
.csrfTokenRequestHandler(xorCsrfRequestHandler)
)
.csrf()
.ignoringRequestMatchers(csrfWhiteList)
.and()
.rememberMe()
.rememberMeServices(dashShiftConfig.tokenBasedRememberMeServicesCore())
.and()
.authorizeHttpRequests(auth -> auth
.dispatcherTypeMatchers(DispatcherType.FORWARD/*, DispatcherType.INCLUDE, DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.REQUEST*/).permitAll()
.requestMatchers("/accessDenied", "/security_check", "/img/*", "/js/*", "/css/*", "/?*", "/", "/login*").permitAll()
.anyRequest().denyAll())
.formLogin()
.loginPage("/")
.usernameParameter("username")
.passwordParameter("password")
.loginProcessingUrl("/security_check")
.failureUrl("/?r=fail")
.failureHandler(HouseAuthFailureHandler())
.successHandler(HouseRedirectAuthenticationSuccessHandler())
.and()
.logout()
.logoutUrl("/logout")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessHandler(customLogoutHandler())
.deleteCookies("JSESSIONID","rm")
.invalidateHttpSession(true)
.and()
.sessionManagement()
.invalidSessionUrl("/?r=invalidSessionUrlLogin")
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler());
http
.headers()
.frameOptions().disable()
.addHeaderWriter(new StaticHeadersWriter("X-FRAME-OPTIONS", "ALLOW-FROM https://www.secure-service.com"));
http
.headers()
.xssProtection(xssProtection -> xssProtection.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK))
.contentSecurityPolicy("form-action 'self'");
http
.sessionManagement((sessions) -> sessions
.requireExplicitAuthenticationStrategy(false)
)
.sessionManagement()
.maximumSessions(1)
.sessionRegistry(sessionRegistry())
.expiredUrl("/?r=sessionExpiredDuplicateLogin");
return http.build();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(this.customUserDetailsService);
authProvider.setPasswordEncoder(customSecurityConfiguration.passwordEncoder());
return authProvider;
}
}
导游是这样说的:
建议 Spring Security 保护所有调度类型。因此,在 6.0 中,Spring Security 更改了此默认值。 所以,最后,更改您的授权规则以过滤所有调度程序类型。
当我保持这样的配置时:
.authorizeHttpRequests(auth -> auth
.dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll()
.requestMatchers("/accessDenied", "/security_check", "/img/*", "/js/*", "/css/*", "/?*", "/", "/login*").permitAll()
.anyRequest().denyAll())
我无法访问具有
@PreAuthorize()
规则的 Spring 控制器,我得到 Access Denied.
所以,我想在这里实现的是:
@PreAuthorize()
约束集的Spring控制器,@PreAuthorize()
约束,拒绝访问它,request,async,error,forward,include
------------- 2023 年 3 月 15 日更新 --------------
参考你的问题,Spring Security分两步处理授权:
在
SecurityFilterChain
Spring 将检查这个特定的过滤器链是否通常负责请求。因此,它将首先检查一个可选的指定http.securityMatchers(matchers -> ...)
,您可以使用它来预过滤一些通用路由。比方说,您想要一个仅用于 api
调用的 FilterChain,这是选择所有这些调用并将它们拖到此过滤器链中的方法。现在你在过滤器链中,Spring 将寻找许可。 在这里你必须明确允许过滤器链中的每条路由。您可以使用通配符概括路由,但无法忽略显式授予此权限。默认情况下,Spring Security 6 将对所有未设置的路由应用denyAll()
。这是从 5.x 到 6.x 的更新中所做的众多更改之一
只有当第一步的检查成功并且路由被允许时,您才可以选择使用方法安全性来细化安全性。除非过滤器链在第一步中找到有效权限,否则不会计算像
PreAuthorize
、PostAuthorize
和其他注释。第二步完全是可选的,但第一步是强制性的。
所以这是行不通的,因为您根本不授予任何业务路线的许可:
.authorizeHttpRequests(auth -> auth
.dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll()
.requestMatchers("/accessDenied", "/security_check", "/img/*", "/js/*", "/css/*", "/?*", "/", "/login*").permitAll()
.anyRequest().denyAll()
你必须像这样设置它:
.authorizeHttpRequests(auth -> auth
.dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll()
.requestMatchers("/accessDenied", "/security_check", "/img/*", "/js/*", "/css/*", "/?*", "/", "/login*").permitAll()
.requestMatchers("/routes_to_restrict_completely_depending_on_method_security/**").permitAll()
.requestMatchers("/routes_to_restrict_additionally_by_method_security/**")..authenticated()
.anyRequest().denyAll()
记住以上几点,您的要求可能不会像您想象的那样工作:
如果控制器没有@PreAuthorize()约束,拒绝访问它,
可能有一种方法可以实现这种行为,当您首先允许专用路线(或一般所有路线)然后在最后一步中,
AuthorizationFilter
将发挥作用。但老实说,我自己还没有想出如何用 Spring Security 6 做到这一点。