Spring Boot应用启动失败:AuthenticationManager Bean循环依赖求助
Spring Boot电商后台安全配置循环依赖问题解决
错误日志
2022-12-31T00:48:58.789+05:30 ERROR 10168 --- [ restartedMain] o.s.b.d.LoggingFailureAnalysisReporter :
应用启动失败
描述:
应用上下文(application context)中部分Bean的依赖形成循环:
┌──->──┐
| authenticationManager 定义在类路径资源 [com/ecommerce/admin/config/AdminConfiguration.class]
└──<-──┘处理建议:
尽管允许循环引用,但无法打破Bean之间的依赖循环。请更新应用以移除该依赖循环。
进程已结束,退出代码 0
问题代码(AdminConfiguration)
package com.ecommerce.admin.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.DefaultSecurityFilterChain; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @EnableWebSecurity @Configuration public class AdminConfiguration { @Bean public UserDetailsService userDetailsService() { return new AdminServiceConfig(); } @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public AuthenticationManager authenticationManager(AuthenticationManager authenticationManager) throws Exception { return authenticationManager; } @Bean public DaoAuthenticationProvider daoAuthenticationProvider() { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setUserDetailsService(userDetailsService()); provider.setPasswordEncoder(passwordEncoder()); return provider; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeHttpRequests() .and() .formLogin() .loginPage("/login") .loginProcessingUrl("/do-login") .defaultSuccessUrl("/admin/index") .permitAll() .and() .logout() .invalidateHttpSession(true) .clearAuthentication(true) .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) .logoutSuccessUrl("/login/logout") .permitAll(); DefaultSecurityFilterChain build = http.build(); return build; } }
问题原因
循环依赖直接源于错误的authenticationManager Bean定义:该方法将AuthenticationManager作为入参,同时返回该实例,相当于Bean创建时需要先拿到自身实例,形成自循环依赖。
解决方案
方案1:正确构建AuthenticationManager(推荐,适配Spring Security 5.7+)
修改authenticationManager的Bean定义,通过AuthenticationManagerBuilder结合已定义的DaoAuthenticationProvider构建实例,同时补充资源授权规则:
package com.ecommerce.admin.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; 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.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @EnableWebSecurity @Configuration public class AdminConfiguration { @Bean public UserDetailsService userDetailsService() { return new AdminServiceConfig(); } @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } // 正确构建AuthenticationManager,避免循环依赖 @Bean public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception { AuthenticationManagerBuilder authBuilder = http.getSharedObject(AuthenticationManagerBuilder.class); authBuilder.authenticationProvider(daoAuthenticationProvider()); return authBuilder.build(); } @Bean public DaoAuthenticationProvider daoAuthenticationProvider() { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setUserDetailsService(userDetailsService()); provider.setPasswordEncoder(passwordEncoder()); return provider; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeHttpRequests(auth -> auth // 放行登录相关路径,其余路径需认证 .requestMatchers("/login", "/login/logout").permitAll() .anyRequest().authenticated() ) .formLogin(form -> form .loginPage("/login") .loginProcessingUrl("/do-login") .defaultSuccessUrl("/admin/index") .permitAll() ) .logout(logout -> logout .invalidateHttpSession(true) .clearAuthentication(true) .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) .logoutSuccessUrl("/login/logout") .permitAll() ); return http.build(); } }
方案2:移除多余的AuthenticationManager Bean
如果业务代码中没有手动注入AuthenticationManager的需求,可直接删除错误的authenticationManager方法,Spring Security会自动构建并使用合适的实例。
内容的提问来源于stack exchange,提问作者Gopal Marathe
相关产品推荐
相关产品推荐

