升级Spring Boot 3.4.1后Spring Security过滤器链冲突问题咨询
问题背景
在Spring Boot 3.3.x及之前版本,存在两个独立的SecurityFilterChain Bean:
- 认证专用FilterChain(Order=10):处理请求认证逻辑,配置了白名单放行和OAuth2资源服务器
@Bean @Order(10) public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authorizeRequests -> authorizeRequests .requestMatchers(createAntRequestMatchers(whitelist)) .permitAll().anyRequest() .authenticated() ) .oauth2ResourceServer( ...) return http.build(); } - 资源过滤器专用FilterChain(Order=100):添加自定义
ResourceFilter,同时重复配置了相同的认证规则@Bean @Order(100) public SecurityFilterChain filterChain(HttpSecurity http, ResourceFilter resourceFilter) throws Exception { return http .authorizeHttpRequests(authorizeRequests -> authorizeRequests .requestMatchers(createAntRequestMatchers(whitelist)) .permitAll().anyRequest() .authenticated() ).addFilterAfter(resourceFilter, SessionManagementFilter.class).build(); }
升级到Spring Boot 3.4.1后,启动时报错:
A filter chain that matches any request [DefaultSecurityFilterChain defined as 'filterChain' in ... has already been configured, which means that this filter chain ... will never get invoked. Please use
HttpSecurity#securityMatcherto ensure that there is only one filter chain configured for 'any request' and that the 'any request' filter chain is published last.
添加http.securityMatcher("/**")到两个FilterChain后可正常启动,但对该方案的合理性存疑。
当前方案的本质问题
你当前给两个FilterChain都配置securityMatcher("/**")的做法,只是绕过了Spring Security的启动校验,但第二个Order=100的FilterChain永远不会被执行:
Spring Security的FilterChain是按@Order优先级排序的,请求会被第一个匹配到的FilterChain处理,后续的FilterChain不会再介入。因此你添加的ResourceFilter实际上并没有生效,只是避免了启动报错而已。
这个问题的根源是:Spring Boot 3.4.x对应的Spring Security 6.3.x引入了无效FilterChain校验机制——旧版本中,多个未指定securityMatcher的FilterChain默认都匹配所有请求,但只有优先级最高的那个会工作,其余都是无效配置;新版本直接检测这种情况并报错,避免资源浪费。
正确解决方案
根据你的需求(给所有请求添加ResourceFilter+保留认证逻辑),推荐两种合理方案:
方案1:合并为单一FilterChain
这是最直接的方式,把认证逻辑和自定义过滤器配置到同一个FilterChain中,确保所有请求都能经过认证和自定义过滤器:
@Bean @Order(10) public SecurityFilterChain filterChain(HttpSecurity http, ResourceFilter resourceFilter) throws Exception { http .authorizeHttpRequests(authorizeRequests -> authorizeRequests .requestMatchers(createAntRequestMatchers(whitelist)) .permitAll() .anyRequest().authenticated() ) .oauth2ResourceServer(...) .addFilterAfter(resourceFilter, SessionManagementFilter.class); return http.build(); }
方案2:拆分FilterChain并指定不同匹配规则
如果确实需要拆分两个FilterChain(比如分别处理不同路径的请求),必须给每个FilterChain配置专属的securityMatcher规则,确保各自处理对应请求:
// 处理API请求的认证FilterChain @Bean @Order(10) public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception { http .securityMatcher("/api/**") .authorizeHttpRequests(authorizeRequests -> authorizeRequests .requestMatchers(createAntRequestMatchers(whitelist)) .permitAll() .anyRequest().authenticated() ) .oauth2ResourceServer(...); return http.build(); } // 处理静态资源的FilterChain,添加自定义过滤器 @Bean @Order(100) public SecurityFilterChain resourceFilterChain(HttpSecurity http, ResourceFilter resourceFilter) throws Exception { http .securityMatcher("/static/**") .authorizeHttpRequests(authorizeRequests -> authorizeRequests.anyRequest().permitAll() ) .addFilterAfter(resourceFilter, SessionManagementFilter.class); return http.build(); }
总结
你当前的方案属于无效配置,无法达到添加ResourceFilter的预期效果。建议根据实际业务场景,选择合并FilterChain或给每个FilterChain指定专属匹配规则的方案。
内容的提问来源于stack exchange,提问作者Wladimir Diskowski

