I have a spring-boot application using spring-security. The security configuration is split into multiple instances of WebSecurityConfigurerAdapter
.
I have one where I configure logout in general:
@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 }
And there is another WebSecurityConfigurerAdapter
, where I want to do almost nothing, except adding another 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!"); }); }
Both configure()
methods are called. However, if I do log out, only the first LogoutHandler
is called. Changing the @Order
of both configurations does not change the result.
What is missing in my configuration?
3 Answers
Answers 1
When you create several security configurations Spring Boot will create a separate SecurityFilterChain for each of them. See WebSecurity:
@Override protected Filter performBuild() throws Exception { // ... for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) { securityFilterChains.add(securityFilterChainBuilder.build()); } // ... }
When application gets logout request FilterChainProxy will return only one 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; }
If you really need modular security configuration I would suggest to create a separate security configuration for logout and other realms. You can define logout handlers as beans (using @Bean
annotation) in different configuration classes and collect these handlers in logout configuration:
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!"); }; } }
Answers 2
You should be solving this problem with the CompositeLogoutHandler on your single /logout
operation endpoint.
You can still keep two WebSecurityConfigurerAdapter's as desired, but you'll be conglomerating the logout functionality for two LogoutHandler
s into a single composite action:
new CompositeLogoutHandler(loggedOutHandler1, loggedOutHandler2);
Answers 3
The keypoint is you should create separated instance of AuthenticationManger.
0 comments:
Post a Comment