Spring Security 5循环依赖问题:Bean创建时出现不可解析循环引用
Spring Security循环依赖错误解决办法
错误原因
你遇到的BeanCurrentlyInCreationException循环依赖问题,根源出在configureGlobal方法里直接调用passwordEncoder()这个@Bean方法。Spring处理配置类时会生成CGLIB代理,当你在配置类内部调用@Bean方法时,会触发容器去获取该bean实例,但此时SpringSecurityConfig本身还在创建过程中,就会触发循环依赖检测,抛出这个错误。
解决办法
方案1:通过方法参数注入依赖(推荐)
把userDetailsService和passwordEncoder作为参数注入到configureGlobal方法中,让Spring自动处理依赖关系,避免直接调用@Bean方法:
package com.oauth.implementation.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import com.oauth.implementation.service.DefaultUserService; @Configuration @EnableWebSecurity public class SpringSecurityConfig { @Autowired private AuthenticationSuccessHandler successHandler; @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth, DefaultUserService userDetailsService, BCryptPasswordEncoder passwordEncoder) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder); } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/registration/**", "/login/**").permitAll() .anyRequest().authenticated() .and() .formLogin().loginPage("/login").successHandler(successHandler) .logout().logoutUrl("/logout").logoutSuccessUrl("/login") .and().oauth2Login().loginPage("/login").successHandler(successHandler); return http.build(); } }
方案2:注入PasswordEncoder到配置类
在配置类中通过@Autowired注入BCryptPasswordEncoder,然后在configureGlobal中使用这个注入的实例:
package com.oauth.implementation.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import com.oauth.implementation.service.DefaultUserService; @Configuration @EnableWebSecurity public class SpringSecurityConfig { @Autowired private DefaultUserService userDetailsService; @Autowired private AuthenticationSuccessHandler successHandler; @Autowired private BCryptPasswordEncoder passwordEncoder; @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder); } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/registration/**", "/login/**").permitAll() .anyRequest().authenticated() .and() .formLogin().loginPage("/login").successHandler(successHandler) .logout().logoutUrl("/logout").logoutSuccessUrl("/login") .and().oauth2Login().loginPage("/login").successHandler(successHandler); return http.build(); } }
额外优化
原代码里重复调用了csrf().disable(),可以去掉其中一个,避免冗余配置。
内容的提问来源于stack exchange,提问作者Swapnil
相关产品推荐
相关产品推荐

