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的初始化,从而形成闭环:
- Spring尝试创建
securityConfig,需要注入initialAuthenticationFilter; initialAuthenticationFilter依赖authManager;authManager的创建需要HttpSecurity参数;- 创建
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
相关产品推荐
相关产品推荐

