Spring Boot 2.7 SecurityFilterChain替换WebSecurityConfigurerAdapter过滤器问题
问题根因
你改造后过滤器不触发有三个核心问题:
- 链式调用顺序错误:你将路径权限匹配规则(
regexMatchers().permitAll()/regexMatchers().hasAnyAuthority())写在了authorizeRequests()方法之外,这在SecurityFilterChain模式下属于无效配置,会打乱HttpSecurity的构建顺序,导致你注册的自定义过滤器根本没有被挂载到最终的安全过滤器链上。旧版WebSecurityConfigurerAdapter对这类写法有容错处理,新版中已经移除了相关兼容逻辑。 - 多链场景未指定优先级:如果你存在多套安全配置对应多个
SecurityFilterChain,没有通过@Order指定优先级的话,框架会按照Bean初始化顺序随机匹配链,大概率会被范围更大的兜底链提前拦截,你配置在子链上的过滤器和规则都不会执行。 - 过滤器实例化方式有隐患:直接在配置方法中
new过滤器实例,若过滤器类被Spring扫描为全局Bean,会出现重复注册、注册位置错乱的问题。
可行改造方案
以下方案适配多套安全配置、多自定义过滤器的场景:
1. 基础规则
- 每个
SecurityFilterChain只负责一个明确的路径匹配范围,路径匹配精度越高的链,设置的@Order值越小,优先级越高。 - 所有路径权限规则(
permitAll/hasAuthority等)必须写在.authorizeRequests()的调用链范围内,不能和http.antMatcher()(当前链的全局路径匹配)写在同级。 - 自定义过滤器优先声明为配置类内的Bean,再注入到链中,避免全局注册冲突。
2. 代码示例
自定义过滤器
不需要加@Component注解,避免被全局扫描注册:
public class TestFilter extends OncePerRequestFilter { public static final String ROLE_USER = "TESTUSER"; public static final String ROLE_USER_INACTIVE = "TESTUSER_INACTIVE"; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 自定义业务逻辑 filterChain.doFilter(request, response); } }
对应/api/path/**路径的安全配置
@EnableWebSecurity @Configuration // 优先级设为1,比兜底链优先级高,数字越小优先级越高 @Order(1) public class TestPathSecurityConfig { // 在当前配置类内声明过滤器Bean,仅对当前链生效 @Bean public TestFilter testFilter() { return new TestFilter(); } @Bean public SecurityFilterChain testPathFilterChain(HttpSecurity http) throws Exception { // 指定当前链仅拦截/api/path/**下的请求 http.antMatcher("/api/path/**") // 按需配置CSRF、CORS规则 .csrf().disable() // 注册过滤器到指定位置 .addFilterAfter(testFilter(), AbstractPreAuthenticatedProcessingFilter.class) // 配置无状态会话策略 .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() // 所有权限规则必须写在authorizeRequests()之后 .authorizeRequests() .regexMatchers(API_BASE_REGEX + "/ping").permitAll() .regexMatchers(API_BASE_REGEX + "/registrations").hasAnyAuthority(TestFilter.ROLE_USER, TestFilter.ROLE_USER_INACTIVE) .anyRequest().hasAuthority(TestFilter.ROLE_USER); return http.build(); } }
其他路径的独立安全配置示例
比如管理后台路径的独立配置,优先级比上面的业务路径稍低即可:
@Configuration @Order(2) public class AdminPathSecurityConfig { @Bean public AdminFilter adminFilter() { return new AdminFilter(); } @Bean public SecurityFilterChain adminPathFilterChain(HttpSecurity http) throws Exception { http.antMatcher("/api/admin/**") .addFilterAfter(adminFilter(), AbstractPreAuthenticatedProcessingFilter.class) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .anyRequest().hasRole("ADMIN"); return http.build(); } }
兜底默认安全链
优先级设为最低,处理所有未被前面链匹配的请求:
@Configuration public class DefaultSecurityConfig { @Bean @Order(Ordered.LOWEST_PRECEDENCE) public SecurityFilterChain defaultFilterChain(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin(); return http.build(); } }
3. 特殊场景处理
如果你的自定义过滤器已经加了@Component注解被全局扫描,需要添加如下配置禁用过滤器的全局Servlet注册,避免过滤器重复执行、不执行:
@Bean public FilterRegistrationBean<TestFilter> disableTestFilterGlobalRegistration(TestFilter testFilter) { FilterRegistrationBean<TestFilter> registration = new FilterRegistrationBean<>(testFilter); registration.setEnabled(false); return registration; }
内容的提问来源于stack exchange,提问作者SamuelFilet
相关产品推荐
相关产品推荐

