Spring Security多安全配置下如何让Spring托管自定义过滤器按配置隔离生效
核心问题根因
- Spring Boot 内置自动注册规则:只要Spring容器中存在直接/间接实现
javax.servlet.Filter的Bean,会自动将其挂载到Servlet容器的全局过滤器链,完全不受Spring Security配置管控,这就是你删除所有Security侧显式挂载代码后,两个自定义过滤器依然被全局触发的根本原因。 - 你之前显式挂载时两个过滤器同时触发的原因:全局注册的过滤器会先执行一次,Security配置链里挂载的过滤器又会执行一次,相当于同一个请求被过滤器重复处理。
解决方案
第一步:禁用自定义过滤器的全局自动注册
保留过滤器上的@Component注解(保证Spring托管能力,支持@Value环境变量注入),通过FilterRegistrationBean关闭Spring Boot的自动注册逻辑,让过滤器只存在于Spring容器中、不进入全局Servlet过滤器链:
在你的WebSecurityConfig类中新增两个Bean,分别对应两个自定义过滤器:
// 关闭TestFilter1的全局自动注册 @Bean public FilterRegistrationBean<TestFilter1> testFilter1AutoRegDisable(TestFilter1 filter1) { FilterRegistrationBean<TestFilter1> regBean = new FilterRegistrationBean<>(filter1); regBean.setEnabled(false); return regBean; } // 关闭TestFilter2的全局自动注册 @Bean public FilterRegistrationBean<TestFilter2> testFilter2AutoRegDisable(TestFilter2 filter2) { FilterRegistrationBean<TestFilter2> regBean = new FilterRegistrationBean<>(filter2); regBean.setEnabled(false); return regBean; }
第二步:为每个安全配置绑定专属过滤器
拆分的两个WebSecurityConfigurerAdapter的HttpSecurity实例是相互独立的,通过构造器注入对应专属的过滤器,在配置方法中显式挂载到当前Security过滤链即可:
修改后的完整安全配置代码:
@EnableWebSecurity(debug = true) public class WebSecurityConfig { // 第一步提到的两个全局注册禁用Bean,写在这里即可 @Bean public FilterRegistrationBean<TestFilter1> testFilter1AutoRegDisable(TestFilter1 filter1) { FilterRegistrationBean<TestFilter1> regBean = new FilterRegistrationBean<>(filter1); regBean.setEnabled(false); return regBean; } @Bean public FilterRegistrationBean<TestFilter2> testFilter2AutoRegDisable(TestFilter2 filter2) { FilterRegistrationBean<TestFilter2> regBean = new FilterRegistrationBean<>(filter2); regBean.setEnabled(false); return regBean; } @Order(1) @Configuration public static class Config1 extends WebSecurityConfigurerAdapter { // 构造器注入v1版本专属的TestFilter1 private final TestFilter1 v1Filter; public Config1(TestFilter1 v1Filter) { this.v1Filter = v1Filter; } @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(HttpSecurity http) throws Exception { System.out.println("******** Config1:config"); http.antMatcher("/api/v1/**") // 将专属过滤器挂载到当前Security链,位置为BasicAuthenticationFilter之前 .addFilterBefore(v1Filter, BasicAuthenticationFilter.class) .authorizeRequests() .antMatchers("/api/v1/greet").permitAll() .anyRequest().authenticated(); } } @Order(2) @Configuration public static class Config2 extends WebSecurityConfigurerAdapter { // 构造器注入v2版本专属的TestFilter2 private final TestFilter2 v2Filter; public Config2(TestFilter2 v2Filter) { this.v2Filter = v2Filter; } @Override protected void configure(HttpSecurity http) throws Exception { System.out.println("******** Config2:config"); http.antMatcher("/api/v2/**") // 将专属过滤器挂载到当前Security链 .addFilterBefore(v2Filter, BasicAuthenticationFilter.class) .authorizeRequests() .antMatchers("/api/v2/greet").permitAll() .anyRequest().authenticated(); } } }
验证效果
- 访问
/api/v1/**路径时,只会触发TestFilter1的逻辑,不会执行TestFilter2 - 访问
/api/v2/**路径时,只会触发TestFilter2的逻辑,不会执行TestFilter1 - 两个过滤器上的
@Value注入字段可以正常读取环境变量,符合Spring托管的要求
注意事项
- 不要将同一个过滤器同时挂载到多个Security配置类中,否则会跨路径重复触发
- 如果后续升级到Spring Security 6.x(
WebSecurityConfigurerAdapter已废弃),核心逻辑不变:定义独立的SecurityFilterChainBean,通过addFilterBefore挂载对应过滤器,同时保留FilterRegistrationBean禁用全局注册即可。
内容的提问来源于stack exchange,提问作者Konaras
相关产品推荐
相关产品推荐

