Spring Security AuthenticationManager出现StackOverflowError问题求助
解决Spring Security JWT认证中的StackOverflowError问题
问题分析
你遇到的StackOverflowError主要由以下配置和代码逻辑问题导致:
- SecurityConfig配置冲突:继承
SecurityConfigurerAdapter的同时手动定义SecurityFilterChain,引发Spring Security内部配置循环依赖。 - AuthenticationManager关联缺失:未在Security上下文绑定UserDetailsService,导致认证流程出现循环调用。
- 冗余数据库查询:登录方法中重复查询数据库,虽不直接引发栈溢出,但不符合规范且可能隐含其他问题。
修正方案
1. 重构SecurityConfig配置
移除不必要的SecurityConfigurerAdapter继承,明确关联UserDetailsService:
@Configuration @EnableMethodSecurity(securedEnabled = true) public class SecurityConfig { private final UserDetailsService userDetailsService; // 注入你的UserDetailsService实现类(UserAppService) public SecurityConfig(UserDetailsService userDetailsService) { this.userDetailsService = userDetailsService; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .cors(Customizer.withDefaults()) .authorizeHttpRequests(auth -> auth.anyRequest().permitAll()) .httpBasic(Customizer.withDefaults()) // 绑定UserDetailsService到Security上下文 .userDetailsService(userDetailsService); return http.build(); } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception { return authConfig.getAuthenticationManager(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
2. 优化登录控制器逻辑
从认证后的Authentication对象直接获取UserDetails,避免冗余查询,并遵循JWT标准格式添加Bearer前缀:
@RestController @RequestMapping("log/auth") public class LogUserController { private final AuthenticationManager authenticationManager; private final JwtUtil jwtUtil; // 移除不必要的UserAppRepository注入 public LogUserController(AuthenticationManager authenticationManager, JwtUtil jwtUtil) { this.authenticationManager = authenticationManager; this.jwtUtil = jwtUtil; } @PostMapping("/login") public ResponseEntity<?> login(@RequestBody LoginDto loginDto) { UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(loginDto.getEmail(), loginDto.getPassword()); // 执行认证,成功后Authentication对象包含已验证的UserDetails Authentication authentication = authenticationManager.authenticate(authToken); UserDetails userDetails = (UserDetails) authentication.getPrincipal(); String jwt = jwtUtil.create(userDetails.getUsername()); // 标准JWT响应头格式:Bearer + Token return ResponseEntity.ok() .header(HttpHeaders.AUTHORIZATION, "Bearer " + jwt) .build(); } }
3. 确保UserDetailsService实现正确
你的UserAppService需正确实现UserDetailsService,并使用PasswordEncoder处理密码验证:
@Service public class UserAppService implements UserDetailsService { private final UserAppRepository userAppRepository; private final PasswordEncoder passwordEncoder; public UserAppService(UserAppRepository userAppRepository, PasswordEncoder passwordEncoder) { this.userAppRepository = userAppRepository; this.passwordEncoder = passwordEncoder; } @Override public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { UserApp user = userAppRepository.findByEmail(email) .orElseThrow(() -> new UsernameNotFoundException("用户不存在: " + email)); // 返回Spring Security的User对象,包含权限信息 return User.builder() .username(user.getEmail()) .password(user.getPassword()) // 确保数据库中存储的是BCrypt加密后的密码 .roles(user.getRoles().toArray(new String[0])) // 根据你的角色结构调整 .build(); } }
关键注意事项
- 确保数据库中用户密码是通过
BCryptPasswordEncoder加密后的字符串,否则认证会失败。 - 移除
SecurityConfig对SecurityConfigurerAdapter的继承,避免与SecurityFilterChain配置冲突,这是解决StackOverflowError的核心。
内容的提问来源于stack exchange,提问作者Carlos Daniel Lasso Gómez
相关产品推荐
相关产品推荐

