15

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?

Rüdiger Schulz
  • 2,588
  • 3
  • 27
  • 43
  • 1
    Are you trying to register two logout handlers to the same url or are you trying to specifically have a logout that does not run everything else? – Deadron Nov 01 '17 at 23:05
  • Both LogoutHandlers should run on the same logout. – Rüdiger Schulz Nov 01 '17 at 23:07
  • Try that link, https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#multiple-httpsecurity I think your url are same so. – Ataur Rahman Munna Nov 02 '17 at 05:28
  • I know that link of course, but at what part should I look especially? My point is to modularize the configuration feature-wise. – Rüdiger Schulz Nov 02 '17 at 07:06
  • They have @Order(2) and @Order(3), and changing it does NOT affect which handler is called. It's always the one in the more "wide" configuration. Anyway, I would in both cases expect that both are called. But maybe that's not possible with the current version? – Rüdiger Schulz Nov 02 '17 at 23:37

3 Answers3

10

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!");
        };
    }
}
Maxim Demin
  • 151
  • 4
  • When I have all `LogoutHandler`s in a single place, do I still need the `CompositeLogoutHandler` or couldn't I just add all of them in a loop? Other than that, your answer looks very promising. – Rüdiger Schulz Nov 13 '17 at 10:34
0

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);
Dovmo
  • 8,121
  • 3
  • 30
  • 44
  • 1
    That is the kind of manual wiring I would like to avoid (not much magic happening inside `CompositeLogoutHandler`). I want to provide an independent `WebSecurityConfigurerAdapter`, which will add functionality working by itself. Simplest example would be logging each login and logout. But it seems like `addLogoutHandler` is behaving more like a `setLogoutHandler`. – Rüdiger Schulz Nov 10 '17 at 10:44
0

The keypoint is you should create separated instance of AuthenticationManger.

Here is an sample for multiples WebSecurityAdapter

kidnan1991
  • 366
  • 2
  • 12