You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中AuthenticationManager.authenticate方法触发栈溢出问题排查

Spring Security认证栈溢出问题分析与解决

问题场景

配置Spring Security配置类后,发现authenticationConfiguration.getAuthenticationManager()返回null,且在AuthController调用authenticationManager.authenticate()时触发栈溢出错误。

SecurityConfig代码

package com.example.demo.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private JwtAuthEntryPoint authEntryPoint;
    private CustomUserDetailsService userDetailsService;
    
    @Autowired
    public SecurityConfig(CustomUserDetailsService userDetailsService, JwtAuthEntryPoint authEntryPoint) {
        this.userDetailsService = userDetailsService;
        this.authEntryPoint = authEntryPoint;
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .exceptionHandling()
                .authenticationEntryPoint(authEntryPoint)
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeHttpRequests()
                .requestMatchers("/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .httpBasic();
        http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }

    @Bean
    public UserDetailsService users() {
        UserDetails admin = User.builder()
                .username("admin")
                .password("")
                .roles("ADMIN")
                .build();

        return new InMemoryUserDetailsManager(admin);
    }

    @Bean
    public AuthenticationManager authenticationManager(
            AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public JWTAuthenticationFilter jwtAuthenticationFilter() {
        return new JWTAuthenticationFilter();
    }
}

AuthController代码

@RestController
@RequestMapping("/auth")
public class AuthController {
    @Autowired
    private AuthenticationManager authenticationManager;
    private UserRepository userRepository;
    private RoleRepository roleRepository;
    private PasswordEncoder passwordEncoder;
    private JWTGenerator jwtGenerator;

    @Autowired
    public AuthController(AuthenticationManager authenticationManager, UserRepository userRepository,
                          RoleRepository roleRepository, PasswordEncoder passwordEncoder, JWTGenerator jwtGenerator) {
        this.authenticationManager = authenticationManager;
        this.userRepository = userRepository;
        this.roleRepository = roleRepository;
        this.passwordEncoder = passwordEncoder;
        this.jwtGenerator = jwtGenerator;
    }

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody User user){
        Authentication authentication = this.authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        user.getUsername(),
                        user.getPassword()));
        SecurityContextHolder.getContext().setAuthentication(authentication);
        String token = jwtGenerator.generateToken(authentication);
        return new ResponseEntity<>(HttpStatus.OK);
    }
}

问题根源

  1. UserDetailsService Bean冲突:配置类中同时注入了CustomUserDetailsService,又定义了users()方法返回InMemoryUserDetailsManager,容器中存在两个UserDetailsService实例,导致AuthenticationConfiguration无法确定依赖,无法正确初始化AuthenticationManager,出现返回null的情况。
  2. 密码未加密:InMemory用户的密码是空字符串且未经过BCryptPasswordEncoder加密,认证时密码校验不通过,触发异常处理逻辑,结合AuthenticationManager的初始化异常,最终引发循环调用导致栈溢出。
  3. 过滤器潜在循环调用:如果JWTAuthenticationFilter内部存在触发认证的逻辑,结合AuthenticationManager的异常状态,可能形成循环调用链条,加剧栈溢出问题。

解决步骤

  1. 删除重复的UserDetailsService Bean:移除SecurityConfig中的users()方法,让Spring使用注入的CustomUserDetailsService作为唯一的用户详情服务实现。
  2. 修复密码加密逻辑:如果需要保留InMemory用户,必须用配置的PasswordEncoder加密密码,示例代码:
    // 替换原users()方法中的密码设置
    .password(passwordEncoder().encode("your-admin-password"))
    
  3. 验证AuthenticationManager初始化:移除冲突Bean后,AuthenticationConfiguration会自动关联正确的UserDetailsService,getAuthenticationManager()将返回有效实例。
  4. 检查JWT过滤器逻辑:确保JWTAuthenticationFilter中没有重复调用authenticationManager.authenticate(),避免触发循环认证流程。

内容的提问来源于stack exchange,提问作者felipebubu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 19:40:02