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); } }
问题根源
- UserDetailsService Bean冲突:配置类中同时注入了
CustomUserDetailsService,又定义了users()方法返回InMemoryUserDetailsManager,容器中存在两个UserDetailsService实例,导致AuthenticationConfiguration无法确定依赖,无法正确初始化AuthenticationManager,出现返回null的情况。 - 密码未加密:InMemory用户的密码是空字符串且未经过BCryptPasswordEncoder加密,认证时密码校验不通过,触发异常处理逻辑,结合AuthenticationManager的初始化异常,最终引发循环调用导致栈溢出。
- 过滤器潜在循环调用:如果JWTAuthenticationFilter内部存在触发认证的逻辑,结合AuthenticationManager的异常状态,可能形成循环调用链条,加剧栈溢出问题。
解决步骤
- 删除重复的UserDetailsService Bean:移除SecurityConfig中的
users()方法,让Spring使用注入的CustomUserDetailsService作为唯一的用户详情服务实现。 - 修复密码加密逻辑:如果需要保留InMemory用户,必须用配置的PasswordEncoder加密密码,示例代码:
// 替换原users()方法中的密码设置 .password(passwordEncoder().encode("your-admin-password")) - 验证AuthenticationManager初始化:移除冲突Bean后,AuthenticationConfiguration会自动关联正确的UserDetailsService,
getAuthenticationManager()将返回有效实例。 - 检查JWT过滤器逻辑:确保JWTAuthenticationFilter中没有重复调用
authenticationManager.authenticate(),避免触发循环认证流程。
内容的提问来源于stack exchange,提问作者felipebubu
相关产品推荐
相关产品推荐

