Autowired注入AuthenticationManager为何抛出StackOverflowError?
集成JWT的Spring Boot应用中AuthenticationManager注入导致StackOverflowError的解决方法
问题场景
开发集成JWT安全机制的Spring Boot应用时,计划通过Spring Security的AuthenticationManager实现身份验证。在配置类中创建了AuthenticationManager Bean,在RestController中通过@Autowired注入后,调用时抛出StackOverflowError。
代码示例
Controller类
@RestController public class AuthenticationController { @Autowired AuthenticationManager authenticationManager; @GetMapping("/sign-in") public String signIn() { System.out.println(authenticationManager); return "Hello World"; } }
Configuration类
@Configuration public class JwtConfiguration { @Bean public AuthenticationManager authenticationManager( final AuthenticationConfiguration authenticationConfiguration) throws Exception { return authenticationConfiguration.getAuthenticationManager(); } @Bean public SecurityFilterChain configure(final HttpSecurity http) throws Exception { http.authorizeHttpRequests() .requestMatchers("/sign-in").permitAll(); return http.build(); } }
错误信息
java.lang.StackOverflowError: null at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:163) ~[spring-aop-6.0.7.jar:6.0.7] at jdk.proxy2/jdk.proxy2.$Proxy58.toString(Unknown Source) ~[na:na] at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:577) ~[na:na] at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343) ~[spring-aop-6.0.7.jar:6.0.7] at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:211) ~[spring-aop-6.0.7.jar:6.0.7] at jdk.proxy2/jdk.proxy2.$Proxy58.toString(Unknown Source) ~[na:na] ...(重复递归调用)
原因分析
问题根源在于自定义的AuthenticationManager Bean与Spring Security自动配置的Bean产生了代理嵌套:
AuthenticationConfiguration.getAuthenticationManager()返回的对象本身已经是Spring AOP代理对象- 将该代理对象再次注册为自定义Bean后,Spring会为这个Bean再次生成代理,形成代理嵌套
- 当调用
System.out.println(authenticationManager)时,触发了代理对象的toString()方法,导致代理之间无限递归调用,最终抛出栈溢出错误
解决方案
方案1:移除自定义的AuthenticationManager Bean
Spring Security已经通过AuthenticationConfiguration自动配置了AuthenticationManager Bean,无需手动注册。直接在Controller中注入即可:
修改后的JwtConfiguration类:
@Configuration public class JwtConfiguration { @Bean public SecurityFilterChain configure(final HttpSecurity http) throws Exception { http.authorizeHttpRequests() .requestMatchers("/sign-in").permitAll(); return http.build(); } }
方案2:避免触发代理的无限递归(可选)
如果确实需要保留自定义Bean,且要打印AuthenticationManager,可以获取代理的目标对象后再打印:
import org.springframework.aop.support.AopUtils; @GetMapping("/sign-in") public String signIn() { try { AuthenticationManager targetManager = (AuthenticationManager) AopUtils.getTargetObject(authenticationManager); System.out.println(targetManager); } catch (Exception e) { e.printStackTrace(); } return "Hello World"; }
方案3:自定义AuthenticationManager的正确方式(如需扩展)
如果需要自定义AuthenticationManager的逻辑(比如配置自定义UserDetailsService),应该通过SecurityFilterChain关联配置:
@Configuration public class JwtConfiguration { @Autowired private UserDetailsService userDetailsService; @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception { AuthenticationManagerBuilder authBuilder = authConfig.getAuthenticationManagerBuilder(); authBuilder.userDetailsService(userDetailsService) .passwordEncoder(passwordEncoder()); return authBuilder.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public SecurityFilterChain configure(final HttpSecurity http) throws Exception { http.authorizeHttpRequests() .requestMatchers("/sign-in").permitAll() .anyRequest().authenticated() .and() .authenticationManager(authenticationManager(authConfig)); return http.build(); } }
内容的提问来源于stack exchange,提问作者Jorian Koning
相关产品推荐
相关产品推荐

