You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Security中SecurityConfiguration与自定义过滤器循环依赖问题

Spring Security循环依赖与AuthenticationManager注入问题解决

问题根源分析

你遇到的循环依赖本质是:SecurityConfiguration 构造注入了 CredentialsAuthenticationFilter,而 CredentialsAuthenticationFilter 依赖 AuthenticationManager;同时 AuthenticationManager 的创建依赖 AuthenticationConfiguration,后者又会引用你在 SecurityConfiguration 中定义的 AuthenticationProvider,形成闭环。

添加@Lazy无效的原因是:CredentialsAuthenticationFilter被标记为@Component,Spring启动时会优先实例化它,此时AuthenticationManager的Bean还处于创建流程中,懒加载的代理尚未完成初始化,而UsernamePasswordAuthenticationFilter的父类AbstractAuthenticationProcessingFilter在初始化阶段会强制校验authenticationManager非空,直接抛出"authenticationManager must be specified"错误。


解决方案

方案1:手动在SecurityConfiguration中声明Filter Bean(推荐)

移除CredentialsAuthenticationFilter上的@Component注解,改为在配置类中手动创建Bean,完全控制依赖注入顺序,彻底避开循环:

修改CredentialsAuthenticationFilter

public class CredentialsAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    private final AuthenticationManager authenticationManager;
    private final JwtIssuer jwtIssuer;
    private final TokenService tokenService;
    private final UserService userService;

    public CredentialsAuthenticationFilter(
            AuthenticationManager authenticationManager,
            JwtIssuer jwtIssuer,
            TokenService tokenService,
            UserService userService
    ) {
        this.authenticationManager = authenticationManager;
        this.jwtIssuer = jwtIssuer;
        this.tokenService = tokenService;
        this.setRequiresAuthenticationRequestMatcher(
                new AntPathRequestMatcher("/api/v1/users/login", "POST"));
        this.userService = userService;
        // 手动向父类传递AuthenticationManager,确保非空校验通过
        super.setAuthenticationManager(authenticationManager);
    }

    // 其余方法保持不变
}

修改SecurityConfiguration

@EnableWebSecurity
@Configuration
@RequiredArgsConstructor
public class SecurityConfiguration {
    private final JwtAuthenticationFilter jwtAuthenticationFilter;
    private final CustomUserDetailsService userDetailsService;
    private final LogoutHandler logoutHandler;
    // 注入创建Filter所需的其他依赖
    private final JwtIssuer jwtIssuer;
    private final TokenService tokenService;
    private final UserService userService;

    // 声明CredentialsAuthenticationFilter的Bean
    @Bean
    public CredentialsAuthenticationFilter credentialsAuthenticationFilter(AuthenticationManager authenticationManager) {
        return new CredentialsAuthenticationFilter(authenticationManager, jwtIssuer, tokenService, userService);
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .cors(Customizer.withDefaults())
                .csrf(AbstractHttpConfigurer::disable)
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(registry -> registry
                        .requestMatchers(HttpMethod.POST, "/api/v1/trainees",
                                "/api/v1/trainers").permitAll()
                        .anyRequest().authenticated())
                .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
                // 使用配置类中声明的Filter Bean
                .addFilterBefore(credentialsAuthenticationFilter(authenticationManager(null)), UsernamePasswordAuthenticationFilter.class)
                .logout(logout -> logout
                        .logoutUrl("/api/v1/users/logout")
                        .addLogoutHandler(logoutHandler)
                        .logoutSuccessHandler((request, response, authentication) -> {
                            SecurityContextHolder.clearContext();
                        }));
        return http.build();
    }

    // 其余方法保持不变
}

方案2:改用Setter注入AuthenticationManager(兼容现有@Component注解)

如果不想移除@Component,将构造器注入改为Setter注入,配合@Lazy延迟依赖获取,同时手动给父类设置AuthenticationManager:

@Component
public class CredentialsAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    private AuthenticationManager authenticationManager;
    private final JwtIssuer jwtIssuer;
    private final TokenService tokenService;
    private final UserService userService;

    // 仅注入无循环的依赖
    public CredentialsAuthenticationFilter(
            JwtIssuer jwtIssuer,
            TokenService tokenService,
            UserService userService
    ) {
        this.jwtIssuer = jwtIssuer;
        this.tokenService = tokenService;
        this.setRequiresAuthenticationRequestMatcher(
                new AntPathRequestMatcher("/api/v1/users/login", "POST"));
        this.userService = userService;
    }

    // Setter注入,@Lazy延迟加载
    @Lazy
    @Autowired
    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
        // 必须调用父类方法,满足父类的非空校验要求
        super.setAuthenticationManager(authenticationManager);
    }

    // 其余方法保持不变
}

这种方式通过延迟AuthenticationManager的注入时机,避开启动阶段的循环依赖,同时手动完成父类的参数设置,解决报错问题。


内容的提问来源于stack exchange,提问作者j0hnny.b

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 12:45:32