Spring Security升级后如何向自定义Filter注入AuthenticationManager
问题根源
当前写法存在两个核心问题:
- 在
securityFilterChain方法内部直接调用同配置类下的authenticationManager()方法获取实例,不会走Spring Bean代理逻辑,容易拿到未完成初始化的AuthenticationManager对象,甚至触发循环依赖。 - 手动通过
new关键字实例化自定义Filter时,这些实例不归Spring容器管理,类内部的@Value属性注入会直接失效,后续注入其他Spring管理组件也会报错。
另外现有配置还有两个额外的bug:
- 权限匹配规则顺序错误:Spring Security按配置从上到下的顺序执行匹配,你把
/**全放行规则放在权限校验规则前面,会导致后续的ADMIN权限校验、登录校验规则完全不生效,范围越大的匹配规则需要放在越靠后的位置。 - 注册接口路径存在笔误:
/users//register多写了一个斜杠,实际匹配会和预期路径不一致。
正确实现方案
方案一:从HttpSecurity上下文获取已初始化的AuthenticationManager(推荐)
Spring Security构建HttpSecurity时,已经将初始化完成的AuthenticationManager存入上下文共享对象,不需要手动调用Bean方法获取,直接修改配置即可:
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { // 直接从HttpSecurity共享对象中拿可用的AuthenticationManager实例 AuthenticationManager authManager = http.getSharedObject(AuthenticationManager.class); http.csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .addFilter(new CustomAuthenticationFilter(authManager)) .addFilter(new CustomAuthorizationFilter(authManager, userRepository)) .authorizeRequests() .antMatchers(HttpMethod.POST, "/user/users/save", "/login", "/users/register").permitAll() .antMatchers("/user/**").hasAuthority("ADMIN") .antMatchers("/user/**").authenticated() .antMatchers("/**").permitAll(); // 测试用全放行规则移到最后 return http.build(); }
这种写法不需要改动自定义Filter的现有代码,改动量最小。
方案二:将自定义Filter交给Spring容器管理
如果后续需要在自定义Filter中使用更多Spring注入的组件,可以把Filter声明为Spring Bean,直接通过容器注入依赖:
- 修改CustomAuthenticationFilter,加上Spring注解,用构造器注入所有依赖:
@Component public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter { private final AuthenticationManager authenticationManager; private final String secretKey; // 所有依赖直接通过构造器注入,不需要字段注入 public CustomAuthenticationFilter(AuthenticationManager authenticationManager, @Value("${security.key}") String secretKey) { this.authenticationManager = authenticationManager; this.secretKey = secretKey; } // attemptAuthentication、successfulAuthentication方法逻辑保持原有写法即可 }
- 对CustomAuthorizationFilter做同样改造,通过构造器注入AuthenticationManager和UserRepository,加上
@Component注解。 - 修改SecurityConfig,直接注入两个Filter实例加入过滤链:
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http, CustomAuthenticationFilter authFilter, CustomAuthorizationFilter authorizationFilter) throws Exception { http.csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .addFilter(authFilter) .addFilter(authorizationFilter) .authorizeRequests() .antMatchers(HttpMethod.POST, "/user/users/save", "/login", "/users/register").permitAll() .antMatchers("/user/**").hasAuthority("ADMIN") .antMatchers("/user/**").authenticated() .antMatchers("/**").permitAll(); return http.build(); }
注意:Spring Boot默认会将@Component标注的Filter自动注册到全局Servlet过滤链,可能导致过滤器重复执行。如果遇到重复触发的问题,添加如下配置关闭自动注册即可:
@Bean public FilterRegistrationBean<CustomAuthenticationFilter> authFilterRegistration(CustomAuthenticationFilter filter) { FilterRegistrationBean<CustomAuthenticationFilter> registration = new FilterRegistrationBean<>(filter); registration.setEnabled(false); return registration; } @Bean public FilterRegistrationBean<CustomAuthorizationFilter> authorizationFilterRegistration(CustomAuthorizationFilter filter) { FilterRegistrationBean<CustomAuthorizationFilter> registration = new FilterRegistrationBean<>(filter); registration.setEnabled(false); return registration; }
内容的提问来源于stack exchange,提问作者Elen Mouradian
相关产品推荐
相关产品推荐

