Spring boot creates 2 security filter chains instead of 1 in spring boot 3.5.8

1 week ago 5
ARTICLE AD BOX

I'm migrating code from spring boot 2.7.5 to 3.5.8. Before the migration everything worked fine

Here is my internal security configuration in my library

@Configuration @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig { @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(request -> request .requestMatchers("/app").hasAuthority("APP") .requestMatchers("/**").permitAll() ) .httpBasic(Customizer.withDefaults()) .exceptionHandling(exceptionHandling -> exceptionHandling.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))) .headers(headersConfigurer -> headersConfigurer .contentSecurityPolicy(securityPolicyConfigurer -> securityPolicyConfigurer.policyDirectives("*")) .referrerPolicy(referrerPolicyConfigurer -> referrerPolicyConfigurer .policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.NO_REFERRER)) .permissionsPolicyHeader(permissionsPolicyConfig -> permissionsPolicyConfig.policy("self")) .httpStrictTransportSecurity(hstsConfig -> hstsConfig .includeSubDomains(true) .maxAgeInSeconds(SECONDS_IN_YEAR)) .frameOptions(HeadersConfigurer.FrameOptionsConfig::deny)) .headers(headersConfigurer -> headersConfigurer.contentTypeOptions(Customizer.withDefaults())); return http.build(); }

the class gets into the context like this

@ComponentScan public class AppConfiguration { }

Next, I include this library in my project and when I run it I get

Caused by: org.springframework.security.web.UnreachableFilterChainException: A filter chain that matches any request [DefaultSecurityFilterChain defined as 'managementSecurityFilterChain' in [class path resource [org/springframework/boot/actuate/autoconfigure/security/servlet/ManagementWebSecurityAutoConfiguration.class]] matching [any request] and having filters [DisableEncodeUrl, WebAsyncManagerIntegration, SecurityContextHolder, HeaderWriter, Cors, Csrf, Logout, UsernamePasswordAuthentication, DefaultResources, DefaultLoginPageGenerating, DefaultLogoutPageGenerating, BasicAuthentication, RequestCacheAware, SecurityContextHolderAwareRequest, AnonymousAuthentication, ExceptionTranslation, Authorization]] has already been configured, which means that this filter chain [DefaultSecurityFilterChain defined as 'securityFilterChain' in [class path resource [/app/config/SecurityConfig.class]] matching [any request] and having filters [DisableEncodeUrl, WebAsyncManagerIntegration, SecurityContextHolder, HeaderWriter, Logout, BasicAuthentication, RequestCacheAware, SecurityContextHolderAwareRequest, AnonymousAuthentication, SessionManagement, ExceptionTranslation, Authorization]] will never get invoked. Please use `HttpSecurity#securityMatcher` to ensure that there is only one filter chain configured for 'any request' and that the 'any request' filter chain is published last. at app//org.springframework.security.config.annotation.web.builders.WebSecurityFilterChainValidator.checkForAnyRequestRequestMatcher(WebSecurityFilterChainValidator.java:59) at app//org.springframework.security.config.annotation.web.builders.WebSecurityFilterChainValidator.validate(WebSecurityFilterChainValidator.java:47) at app//org.springframework.security.web.FilterChainProxy.afterPropertiesSet(FilterChainProxy.java:178) at app//org.springframework.security.config.annotation.web.builders.WebSecurity.performBuild(WebSecurity.java:351) at app//org.springframework.security.config.annotation.web.builders.WebSecurity.performBuild(WebSecurity.java:98) at app//org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder.doBuild(AbstractConfiguredSecurityBuilder.java:355) at app//org.springframework.security.config.annotation.AbstractSecurityBuilder.build(AbstractSecurityBuilder.java:38) at app//org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration.springSecurityFilterChain(WebSecurityConfiguration.java:132) at app//org.springframework.beans.factory.support.SimpleInstantiationStrategy.lambda$instantiate$0(SimpleInstantiationStrategy.java:172) ... 44 more

This happens because the next bin gets into the context earlier

@AutoConfiguration(before = SecurityAutoConfiguration.class, after = { HealthEndpointAutoConfiguration.class, InfoEndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class, OAuth2ClientWebSecurityAutoConfiguration.class, OAuth2ResourceServerAutoConfiguration.class, Saml2RelyingPartyAutoConfiguration.class }) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnDefaultWebSecurity public class ManagementWebSecurityAutoConfiguration { @Bean @Order(SecurityProperties.BASIC_AUTH_ORDER) SecurityFilterChain managementSecurityFilterChain(Environment environment, HttpSecurity http) throws Exception { http.authorizeHttpRequests((requests) -> { requests.requestMatchers(healthMatcher(), additionalHealthPathsMatcher()).permitAll(); requests.anyRequest().authenticated(); }); if (ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", null)) { http.cors(withDefaults()); } http.formLogin(withDefaults()); http.httpBasic(withDefaults()); return http.build(); }

The error was fixed by adding the following settings to application.yaml

spring: autoconfigure: exclude: > org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration

or

@Bean("managementSecurityFilterChain") @Primary @Order(Ordered.HIGHEST_PRECEDENCE) public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

but it seems to me that this is not entirely correct and may lead to negative consequences. How can only managementSecurityFilterChain be excluded from the context?

Read Entire Article