Wednesday, November 15, 2017

Spring Security - multiple configurations - add LogoutHandler

Leave a Comment

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 LogoutHandlers into a single composite action:

new CompositeLogoutHandler(loggedOutHandler1, loggedOutHandler2); 

Answers 3

The keypoint is you should create separated instance of AuthenticationManger.

Here is an sample for multiples WebSecurityAdapter

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment