Spring Boot对接Active Directory认证输入错误凭证触发无限循环问题
问题分析
输入错误凭证时触发无限循环并最终导致StackOverflowError,核心原因在于自定义认证管理器的构建方式与Spring Security默认机制冲突,结合ActiveDirectoryLdapAuthenticationProvider的错误处理逻辑,引发了递归调用:
- 通过
HttpSecurity的AuthenticationManagerBuilder构建认证管理器,容易与全局认证管理器产生循环依赖; - 启用的
formLogin默认配置会引入额外的认证过滤器和入口点,与自定义REST登录接口冲突; convertSubErrorCodesToExceptions开启后,错误码转换逻辑可能触发重复的LDAP连接尝试。
修复方案
1. 调整WebSecurityConfig配置
移除冲突的formLogin配置,改用全局认证管理器构建方式,并将AD认证提供者注册为独立Bean:
@Configuration @EnableWebSecurity public class WebSecurityConfig { private static final String DOMAIN = "xxxxxx.net"; private static final String URL = "ldap://xxxxxxxx:389"; // 移除末尾斜杠,避免URI解析问题 @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.csrf(AbstractHttpConfigurer::disable) .cors(cors -> cors .configurationSource(request -> { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.setAllowedOrigins(List.of("http://localhost:4200")); corsConfiguration.setAllowedMethods(List.of("POST", "PUT", "GET", "DELETE", "OPTIONS")); corsConfiguration.setAllowedHeaders(List.of("Authorization", "Content-Type")); corsConfiguration.setAllowCredentials(true); return corsConfiguration; })) .authorizeHttpRequests(authorize -> authorize .requestMatchers("/auth/**").permitAll() .anyRequest().authenticated()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)); return http.build(); } // 独立注册AD认证提供者 @Bean public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() { ActiveDirectoryLdapAuthenticationProvider adProvider = new ActiveDirectoryLdapAuthenticationProvider(DOMAIN, URL); adProvider.setConvertSubErrorCodesToExceptions(true); adProvider.setUseAuthenticationRequestCredentials(true); return adProvider; } // 通过全局配置获取认证管理器,避免循环依赖 @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception { return authConfig.getAuthenticationManager(); } }
2. 简化LoginController逻辑
移除手动管理Session的冗余代码,Spring Security会自动处理SecurityContext:
@RestController @RequestMapping("/auth") public class LoginController { @Autowired private AuthenticationManager authenticationManager; @PostMapping("/login") public ResponseEntity<?> login(@RequestBody Login login, HttpServletRequest request) { try { UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(login.getUser(), login.getPassword()); authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); Authentication auth = authenticationManager.authenticate(authToken); SecurityContextHolder.getContext().setAuthentication(auth); return ResponseEntity.ok(Map.of("message", "Login Successful")); } catch (AuthenticationException e) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED) .body(Map.of("error", "Login Failed: " + e.getMessage())); } } }
3. 可选:临时禁用错误码转换
如果问题仍存在,可先关闭错误码转换逻辑,验证是否为该功能导致的循环:
adProvider.setConvertSubErrorCodesToExceptions(false);
内容的提问来源于stack exchange,提问作者Francis
相关产品推荐
相关产品推荐

