我有一个使用spring-security的spring-boot应用程序。安全配置分为 WebSecurityConfigurerAdapter
的多个实例。
我通常在其中配置注销的一个:
@Override
protected void configure(HttpSecurity http) throws Exception {
// configure logout
http
.logout()
.logoutUrl("/logout")
.invalidateHttpSession(true)
.addLogoutHandler((request, response, authentication) -> {
System.out.println("logged out 1!");
})
.permitAll();
// ... more security configuration, e.g. login, CSRF, rememberme
}
还有另一个
WebSecurityConfigurerAdapter
,除了添加另一个LogoutHandler之外,我几乎什么都不做:@Override
protected void configure(HttpSecurity http) throws Exception {
// configure logout
http
.logout()
.logoutUrl("/logout")
.addLogoutHandler((request, response, authentication) -> {
System.out.println("logged out 2!");
});
}
两种
configure()
方法都被调用。但是,如果我注销,则仅调用第一个 LogoutHandler
。更改两个配置的@Order
都不会更改结果。我的配置中缺少什么?
最佳答案
当您创建多个安全配置时,Spring Boot将为每个安全配置创建一个单独的SecurityFilterChain。参见WebSecurity:
@Override
protected Filter performBuild() throws Exception {
// ...
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
// ...
}
当应用程序获得注销请求时,FilterChainProxy将仅返回一个SecurityFilterChain:
private List<Filter> getFilters(HttpServletRequest request) {
for (SecurityFilterChain chain : filterChains) {
// Only the first chain that matches logout request will be used:
if (chain.matches(request)) {
return chain.getFilters();
}
}
return null;
}
如果您确实需要模块化安全性配置,建议您为注销和其他 Realm 创建单独的安全性配置。您可以在不同的配置类中将注销处理程序定义为Bean(使用
@Bean
注释),并在注销配置中收集以下处理程序:WebSecurityLogoutConfiguration.java
@Configuration
@Order(99)
public class WebSecurityLogoutConfiguration extends WebSecurityConfigurerAdapter {
// ALL YOUR LOGOUT HANDLERS WILL BE IN THIS LIST
@Autowired
private List<LogoutHandler> logoutHandlers;
@Override
protected void configure(HttpSecurity http) throws Exception {
// configure only logout
http
.logout()
.logoutUrl("/logout")
.invalidateHttpSession(true)
// USE CompositeLogoutHandler
.addLogoutHandler(new CompositeLogoutHandler(logoutHandlers));
http.csrf().disable(); // for demo purposes
}
}
WebSecurity1Configuration.java
@Configuration
@Order(101)
public class WebSecurity1Configuration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// ... more security configuration, e.g. login, CSRF, rememberme
http.authorizeRequests()
.antMatchers("/secured/**")
.authenticated();
}
// LOGOUT HANDLER 1
@Bean
public LogoutHandler logoutHandler1() {
return (request, response, authentication) -> {
System.out.println("logged out 1!");
};
}
}
WebSecurity2Configuration.java
@Configuration
@Order(102)
public class WebSecurity2Configuration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/**")
.permitAll();
}
// LOGOUT HANDLER 2
@Bean
public LogoutHandler logoutHandler2() {
return (request, response, authentication) -> {
System.out.println("logged out 2!");
};
}
}