CustomAuthenticationProvider的authenticate被调用两次问题求助(已参考相关帖)
authenticate Calls with Empty Password on Second Attempt Hey there, let's break down this frustrating double authentication call issue you're dealing with. You mentioned referencing comments under the post ProviderManager.authenticate called twice for BadCredentialsException but still hitting the same problem—where authenticate gets triggered twice, with valid credentials on the first call and an empty password on the second. Let's walk through likely causes and fixes:
Common Root Causes
- Outdated Security Configuration Conflicts: If you're still using
UserWebSecurityConfigurerAdapter(deprecated in Spring Boot 2.7+), it might clash with Spring's auto-configuredSecurityFilterChain, leading to duplicate request processing. - Accidental Credential Erasure: Spring Security defaults to erasing credentials after successful authentication. If a secondary process triggers re-authentication, it might be working with already-cleared credentials.
- Misconfigured ProviderManager/Multiple Providers: If your setup has multiple registered providers or the
ProviderManageris set to retry on exception, it could trigger a second call even when the first attempt had valid credentials. - Frontend Request Duplication: While less likely given the empty password, a misconfigured frontend (like an undisabled submit button leading to double clicks) could send two requests—though the second would usually carry the same credentials unless there's a form reset issue.
Step-by-Step Fixes & Debugging
1. Update to Modern Security Configuration
Replace the deprecated WebSecurityConfigurerAdapter with SecurityFilterChain to avoid auto-config conflicts:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() ) .formLogin(form -> form .loginProcessingUrl("/login") // Match your form's action URL .permitAll() ); return http.build(); } @Bean public CustomAuthProvider customAuthProvider() { return new CustomAuthProvider(); } }
2. Add Detailed Logging to Trace the Second Call
Insert logging in your CustomAuthProvider to pinpoint where the second call is coming from, including credential state and call stack:
@Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { String username = authentication.getName(); String password = (String) authentication.getCredentials(); // Log credential state System.out.println("Auth triggered for user: " + username + " | Password present? " + (password != null && !password.isBlank())); // Log full call stack to trace origin new RuntimeException("Authentication Call Stack").printStackTrace(); // Your existing validation logic if (!isValidCredentials(username, password)) { throw new BadCredentialsException("Invalid credentials"); } return new UsernamePasswordAuthenticationToken(username, password, Collections.emptyList()); }
The stack trace will show exactly which part of your code/Spring Security is initiating the second call.
3. Disable Credential Erasure (If Needed)
If the second call is using cleared credentials, disable credential erasure in your provider configuration:
// For legacy setups using WebSecurityConfigurerAdapter @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(customAuthProvider()) .eraseCredentials(false); // Preserve credentials post-authentication }
4. Check for Duplicate Configuration Classes
Ensure you don't have multiple @Configuration classes handling Spring Security—this can lead to overlapping filter chains that process the same request twice.
5. Validate Frontend Request Flow
Inspect your frontend code to confirm the login form isn't submitting twice (e.g., button not disabled after first click) or resetting the password field before a second submission. You can also check browser network logs to see if two requests are being sent, and what payload they carry.
Final Notes
Once you've traced the second call's origin with the stack trace, you can target the exact issue—whether it's a config conflict, unintended retry logic, or frontend behavior.
内容的提问来源于stack exchange,提问作者jw-at

