Spring Boot Security POST请求/login报StackOverflowError排查
根因定位
java.lang.StackOverflowError 由AuthenticationManager循环委托引用导致,两个authenticate方法形成无出口的递归调用链:
- 自定义
CustomAuthenticationFilter持有的AuthenticationManager实例,与WebSecurityConfigurerAdapter内部生成的委托类AuthenticationManagerDelegator形成自引用 - 调用链路:Filter触发AuthenticationManagerDelegator.authenticate() → 委托给ProviderManager处理 → ProviderManager未找到可处理认证的Provider时,调用持有的父级AuthenticationManager → 父级实例就是最初的AuthenticationManagerDelegator → 回到链路起点无限循环,直到栈内存耗尽。
高频触发场景
- 重写
WebSecurityConfig的authenticationManagerBean()方法时,未调用super.authenticationManagerBean(),手动构建ProviderManager返回时错误绑定了委托对象 - 给自定义认证Filter设置AuthenticationManager时,未引用容器中暴露的单例Bean,而是重复从HttpSecurity上下文中获取未初始化完成的Manager实例,形成自引用
- 未向AuthenticationManager注册可处理用户名密码认证的DaoAuthenticationProvider,导致ProviderManager直接走父级Manager兜底逻辑,刚好命中循环引用
修复步骤
按以下顺序检查配置,逐点修正:
- 修正AuthenticationManager Bean的暴露逻辑
不要手动new ProviderManager实例,直接调用父类方法生成标准Bean:@Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { // 禁止自定义构建逻辑,直接返回父类生成的实例 return super.authenticationManagerBean(); } - 修正自定义Filter的Manager注入逻辑
给CustomAuthenticationFilter设置AuthenticationManager时,直接引用上一步暴露的容器级Bean,不要从HttpSecurity共享对象中重复获取:@Bean public CustomAuthenticationFilter customAuthenticationFilter() throws Exception { CustomAuthenticationFilter filter = new CustomAuthenticationFilter(); // 直接引用容器中注册的AuthenticationManager单例 filter.setAuthenticationManager(authenticationManagerBean()); // 自定义登录路径 filter.setFilterProcessesUrl("/login"); return filter; } - 补全认证提供者配置
注册DaoAuthenticationProvider,绑定你实现的UserDetailsService和PasswordEncoder,避免ProviderManager走父级兜底逻辑:@Bean public DaoAuthenticationProvider daoAuthenticationProvider() { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setUserDetailsService(yourUserDetailsService); provider.setPasswordEncoder(yourPasswordEncoder); return provider; } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 注册自定义认证提供者,不要留空配置 auth.authenticationProvider(daoAuthenticationProvider()); } - 修正过滤链注册逻辑
将自定义认证Filter添加到过滤链的正确位置,替换默认的UsernamePasswordAuthenticationFilter,避免重复注册:@Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers("/login", "/refresh").permitAll() .anyRequest().authenticated() .and() // 将自定义Filter加入过滤链 .addFilter(customAuthenticationFilter()); }
验证方式
配置修改完成后重启服务,在ProviderManager.authenticate方法入口打调试断点,首次进入时检查当前实例的parent字段值,如果不是WebSecurityConfigurerAdapter$AuthenticationManagerDelegator类型,说明循环引用已经解除,发送POST登录请求即可正常进入认证逻辑,不会再触发栈溢出。
内容的提问来源于stack exchange,提问作者Abhinav Saxena
相关产品推荐
相关产品推荐

