Spring Security 5.7无WebSecurityConfigurerAdapter多认证提供者异常排查
一、仅DaoAuthenticationProvider生效,LdapAuthProvider未被调用的原因及解决
核心原因
Spring Security的AuthenticationManager会按Provider添加顺序依次调用认证提供者:
- 若某个Provider成功完成认证,直接返回结果;
- 若某个Provider抛出
AuthenticationException(如密码错误、用户不存在),流程会直接终止,不会继续调用后续Provider; - 仅当Provider返回
null(表示无法处理当前认证请求)时,才会尝试下一个Provider。
你的场景中,DaoAuthenticationProvider在验证失败时(无论用户是否存在、密码是否正确)都会抛出对应异常,而非返回null,导致LdapAuthProvider完全没有被触发的机会。而移除DaoProvider后,LdapProvider自然成为唯一的选择,因此可以正常工作。
解决方法
自定义DaoAuthenticationProvider,重写authenticate方法,在验证失败时返回null,让AuthenticationManager继续尝试下一个Provider:
public class CustomDaoAuthenticationProvider extends DaoAuthenticationProvider { @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { try { // 尝试正常认证 return super.authenticate(authentication); } catch (AuthenticationException e) { // 认证失败时返回null,交由下一个Provider处理 return null; } } }
然后替换原有的authenticationProvider Bean定义:
@Bean public DaoAuthenticationProvider authenticationProvider() { CustomDaoAuthenticationProvider authProvider = new CustomDaoAuthenticationProvider(); authProvider.setUserDetailsService(userService); authProvider.setPasswordEncoder(passwordEncoder()); return authProvider; }
同时保持Provider的添加顺序(你当前代码中先添加Ldap再添加Dao的顺序是合理的,若想优先尝试Dao,可调换顺序)。
二、全局AuthenticationManager未包含自定义Provider的原因及解决
核心原因
你通过AuthenticationConfiguration.getAuthenticationManager()获取的是Spring Security默认的全局AuthenticationManager,而你添加的Provider仅注册到了SecurityFilterChain对应的局部AuthenticationManager中,全局实例并未包含这些自定义Provider。
解决方法
手动构建全局AuthenticationManager,将两个自定义Provider注册进去:
@Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception { AuthenticationManagerBuilder�man<span// Kaphus支持默认 decorated 其他 Stop处 POJO的,哦不对,正确代码: AuthenticationManagerBuilder authBuilder = authConfig.getAuthenticationManagerBuilder(); // 注册Ldap认证提供者 authBuilder.authenticationProvider(ldapAuthProvider); // 注册自定义Dao认证提供者 authBuilder.authenticationProvider(authenticationProvider()); return authBuilder.build(); }
此时,你通过@Autowired注入的AuthenticationManager就会包含两个自定义Provider,调用authenticate方法时会按注册顺序依次尝试认证。
可选优化
如果你希望SecurityFilterChain也使用这个全局的AuthenticationManager,可以在securityFilterChain方法中指定,避免重复添加Provider:
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception { http .cors() .and().csrf().disable() .headers().frameOptions().disable() .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and().authorizeRequests() .antMatchers("/api/test/**", "/auth/**", "/h2-console/**").permitAll() .anyRequest().authenticated() .and().addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class) // 指定使用全局AuthenticationManager .authenticationManager(authenticationManager); return http.build(); }
内容的提问来源于stack exchange,提问作者HNP

