Kotlin环境Spring调用AuthenticationManager认证抛出StackOverflowError
问题根因
该问题是Kotlin语法特性与Spring Security的AOP代理逻辑冲突导致的无限递归调用:
Kotlin中类、类成员方法默认均为final修饰,不可被继承/重写。你编写的SecurityConfig配置类、重写的authenticationManagerBean()方法均未添加open修饰符解除final限制,导致Spring无法基于CGLIB生成配置类的目标类代理,只能退化为JDK动态代理生成基于接口的代理对象,最终造成AuthenticationManager实例的循环引用,调用authenticate方法时进入无限递归,触发栈溢出。
完全相同的配置在Java环境可正常运行的原因是:Java中类、成员方法默认均为非final,重写的authenticationManagerBean()方法可被CGLIB正常代理,不会出现代理对象循环嵌套的问题。
异常栈对应循环链路
从重复打印的栈帧可以明确看到死循环的调用路径:
- JDK动态代理生成的
$Proxy99.authenticate()方法被触发 - 代理逻辑将调用转发给内部持有的
ProviderManager.authenticate() ProviderManager内部委托的AuthenticationManager实例是WebSecurityConfigurerAdapter$AuthenticationManagerDelegator,继续调用其authenticate()方法AuthenticationManagerDelegator持有的委托对象又回到第一步的JDK动态代理实例,循环往复直到栈空间耗尽抛出StackOverflowError
修复方案
给SecurityConfig类、重写的authenticationManagerBean()方法添加open修饰符,允许CGLIB生成目标类代理即可解决问题,修正后的核心配置代码如下:
@EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) // 类添加open修饰,解除final限制 open class SecurityConfig : WebSecurityConfigurerAdapter() { override fun configure(http: HttpSecurity) { http .csrf().disable() } @Bean // 移除不必要的可空返回值声明 fun passwordEncoder(): BCryptPasswordEncoder = BCryptPasswordEncoder() @Bean // 重写的Bean方法添加open修饰 override fun authenticationManagerBean(): AuthenticationManager = super.authenticationManagerBean() }
内容的提问来源于stack exchange,提问作者trom
相关产品推荐
相关产品推荐

