Describe the bug Spring Security automatically adds a default SecurityFilterChain when no SecurityFilterChain bean has been configured. This conflicts with SecurityFilterChains added via a WebSecurityCustomizer resulting in an UnreachableFilterChainException at application startup because the automatically configured filter chain matches any request.

To Reproduce

@Configuration(proxyBeanMethods = false)
public class SecurityConfiguration {

    @Bean
    public WebSecurityCustomizer security(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(registry -> registry.anyRequest().permitAll());
        var chain = http.build();

        return web -> web.addSecurityFilterChainBuilder(() -> chain);
    }
}

Expected behavior The default SecurityFilterChain should only be added to the WebSecurity if no SecurityBuilder<? extends SecurityFilterChain> is present after adding all SecurityFilterChain beans and applying all WebSecurityCustomizers.

Comment From: SeungyoupBaek

Hi, I’d like to work on this issue.
I can reproduce the problem and plan to update the logic so the default SecurityFilterChain is only added when no other chains are present.
Could you assign this to me? Thanks!

Comment From: jzheaux

Thanks, @jbb01. Can you say more about what you are trying to achieve? I'm hesitate to change logic like this if a more idiomatic way exists. From what I can tell, you are able to do the following:

@Bean 
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests(registry -> registry.anyRequest().permitAll());
    return http.build();
}

This will override the default and be registered with WebSecurity. Why do you need the other approach?

Comment From: jbb01

Thanks for the feedback.

I'm trying to dynamically create zero or more SecurityFilterChains based on external configuration. The only ways I know how to achieve this are - using a WebSecurityCustomizer as described above or - using a BeanDefinitionRegistryPostProcessor to dynamically register the filter chains as beans. This however limits what external configuration can be considered as the post processor is executed early in the lifecycle. - always registering all possible filter chains as beans but replacing the ones not needed with empty filter chains that don't match any request. This approach falls short if, based on config, no custom filter chains are registered as the empty chains prevent registration of the default.

Of those three, using a WebSecurityCustomizer seemed like the cleanest approach. If you know of a better solution, please enlighten me.

As far as I can see, moving the check for already registered filter chains to after the customizers are applied, should be a relatively non-invasive change, as the WebSecurity has no API for querying already registered filter chain builders. Therefore a web security customizer couldn't possibly depend on the default filter chain already being registered when it gets executed.