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

Spring中HttpSecurityConfiguration相关循环依赖问题排查

Spring循环依赖问题分析:UserDetailsService Bean位置引发的循环依赖

在Spring项目中遇到循环依赖错误:当UserDetailsService Bean定义在SecurityConfig类中时触发该错误,将同一个Bean移到AuthenticationConfig类后错误消失,需要明确循环依赖的来源。

相关代码

SecurityConfig类

@Configuration
public class SecurityConfig {

    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;
    @Autowired
    private InitialAuthenticationFilter initialAuthenticationFilter;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {    
        return http.csrf().disable()
                .httpBasic().and()
                .addFilterBefore(initialAuthenticationFilter, BasicAuthenticationFilter.class)
                .addFilterAfter(jwtAuthenticationFilter,BasicAuthenticationFilter.class)
                .authorizeHttpRequests().requestMatchers("/actuator/**").permitAll().and()
                .authorizeHttpRequests().anyRequest().authenticated()
                .and()
                .build();
    }

    @Bean
    public UserDetailsService userDetailsService(){    
        UserDetails user = User.builder()
                .username("user")
                .password("123")
                .roles("USER")
                .build();
        UserDetails admin = User.builder()
                .username("admin")
                .password("123")
                .roles("USER", "ADMIN")
                .build();
        return new InMemoryUserDetailsManager(user, admin);        
    }
}

AuthenticationConfig类

@Configuration
public class AuthenticationConfig {
    @Autowired
    private OtpAuthenticationProvider otpAuthenticationProvider;
    @Autowired
    private UsernamePasswordAuthenticationProvider usernamePasswordAuthenticationProvider;

    @Bean
    public AuthenticationManager authManager(HttpSecurity http) throws Exception {
        AuthenticationManagerBuilder authenticationManagerBuilder =
                http.getSharedObject(AuthenticationManagerBuilder.class);
        authenticationManagerBuilder.authenticationProvider(usernamePasswordAuthenticationProvider).authenticationProvider(otpAuthenticationProvider);
        return authenticationManagerBuilder.build();
    }
}

InitialAuthenticationFilter类

@Component
public class InitialAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private AuthenticationManager manager;

    @Value("${jwt.signing.key}")
    private String signingKey;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException { // do filtering
        }
}

UsernamePasswordAuthenticationProvider类

// 类似的Provider示例
@Component
public class UsernamePasswordAuthenticationProvider implements AuthenticationProvider{
     
    @Autowired
    private AuthenticationServerProxy proxy;
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
         String password = String.valueOf(authentication.getCredentials());
         proxy.sendAuth(username, password);
         return new UsernamePasswordAuthenticationToken(username, password);
    }

    @Override
    public boolean supports(Class<?> authentication) {
        // TODO Auto-generated method stub
        return UsernamePasswordAuthentication.class.isAssignableFrom(authentication);
    }
}

错误日志

Exception: org.springframework.beans.factory.UnsatisfiedDependencyException. 
Message: Error creating bean with name 'securityConfig': Unsatisfied dependency expressed through field 'initialAuthenticationFilter': 
Error creating bean with name 'initialAuthenticationFilter': Unsatisfied dependency expressed through field 'manager': 
Error creating bean with name 'authManager' defined in class path resource [root/AuthenticationConfig.class]: 
Unsatisfied dependency expressed through method 'authManager' parameter 0: 
Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration.httpSecurity' 
defined in class path resource [org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.class]: 
Failed to instantiate [org.springframework.security.config.annotation.web.builders.HttpSecurity]: 
Factory method 'httpSecurity' threw exception with message: Error creating bean with name 'securityConfig': 
Requested bean is currently in creation: Is there an unresolvable circular reference?

循环依赖关系图

The dependencies of some of the beans in the application context form a cycle:
┌─────┐
|  securityConfig (field private root.filters.InitialAuthenticationFilter root.SecurityConfig.initialAuthenticationFilter)
↑     ↓
|  initialAuthenticationFilter (field private org.springframework.security.authentication.AuthenticationManager root.filters.InitialAuthenticationFilter.manager)
↑     ↓
|  authManager defined in class path resource [root/AuthenticationConfig.class]
↑     ↓
|  org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration.httpSecurity defined in class path resource [org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.class]
└─────┘

循环依赖原因解析

核心逻辑

当UserDetailsService定义在SecurityConfig中时,Spring Security的自动配置逻辑会将SecurityConfig识别为核心安全配置类,在构建HttpSecurity的过程中,会触发对该类中所有安全相关Bean的初始化,从而形成闭环:

  1. Spring尝试创建securityConfig,需要注入initialAuthenticationFilter;
  2. initialAuthenticationFilter依赖authManager;
  3. authManager的创建需要HttpSecurity参数;
  4. 创建HttpSecurity时,Spring Security会扫描处理用户定义的安全配置类(即SecurityConfig),此时SecurityConfig正处于创建过程中,循环依赖由此产生。

为什么移走UserDetailsService就解决了问题

将UserDetailsService移到AuthenticationConfig后,SecurityConfig不再包含额外的安全Bean定义,Spring Security在构建HttpSecurity时,无需提前触发SecurityConfig的初始化,打破了循环链。

关于HttpSecurity无直接依赖的疑问

HttpSecurity实例本身并不直接持有securityConfig的引用,但在其创建阶段,Spring Security的配置处理器会扫描所有@Configuration类中的安全相关Bean(如UserDetailsService),这个扫描处理过程会间接触发SecurityConfig的初始化,这就是调试HttpSecurity实例看不到直接依赖的原因——依赖产生于配置处理阶段,而非实例字段引用。

内容的提问来源于stack exchange,提问作者Santino Zanone

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 18:13:11