添加未使用的AuthenticationProvider后Spring Security认证报错排查
原本基于Spring Boot + Spring Security的应用,使用formLogin和InMemoryUserDetailsManager运行完全正常,但添加了一个supports方法始终返回false的MyAuthenticationProvider(标注@Component)后,应用抛出No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken异常。移除@Component注解后,应用又恢复正常。
疑问点:默认的DaoAuthenticationProvider应该负责处理UsernamePasswordAuthenticationToken,为什么仅仅添加这个“闲置”的Provider后,系统就找不到默认的DaoAuthenticationProvider了?
相关代码片段
1. 安全配置类
@Configuration @EnableWebSecurity public class SecurityConfiguration { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests((authorize) -> authorize .anyRequest().authenticated() ) .formLogin(withDefaults()); return http.build(); } @Bean public InMemoryUserDetailsManager userDetailsService() { UserDetails user = User.withDefaultPasswordEncoder() .username("user") .password("password") .roles("USER") .build(); return new InMemoryUserDetailsManager(user); } }
2. 自定义AuthenticationProvider
@Component public class MyAuthenticationProvider implements AuthenticationProvider { @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { throw new InsufficientAuthenticationException("Dummy"); } @Override public boolean supports(Class<?> authentication) { return false; } }
原因解析
Spring Security的ProviderManager(默认的AuthenticationManager实现)会自动收集Spring容器中所有的AuthenticationProvider Bean。核心问题在于Spring Security的自动配置逻辑:
当容器中不存在任何自定义的AuthenticationProvider Bean时,自动配置类会自动创建基于UserDetailsService的DaoAuthenticationProvider,用来处理UsernamePasswordAuthenticationToken(也就是表单登录的认证令牌)。
但一旦你通过@Component将自定义的MyAuthenticationProvider注册到容器中,自动配置逻辑就会认为你要完全自定义认证流程,不再自动创建默认的DaoAuthenticationProvider。
此时ProviderManager的列表里只有你的自定义Provider,而它的supports方法始终返回false,导致遍历所有Provider时,没有任何一个能支持UsernamePasswordAuthenticationToken,最终抛出异常。
解决方法
方法1:手动注册DaoAuthenticationProvider
如果你需要保留自定义Provider,需要手动创建DaoAuthenticationProvider并注册为Bean,让它加入到ProviderManager的认证列表中:
@Bean public DaoAuthenticationProvider daoAuthenticationProvider(InMemoryUserDetailsManager userDetailsService) { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setUserDetailsService(userDetailsService); // 这里使用和你创建用户时一致的密码编码器 provider.setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder()); return provider; }
方法2:移除自定义Provider的@Component注解
如果不需要这个自定义Provider,直接去掉@Component,让Spring Security自动创建默认的DaoAuthenticationProvider即可恢复正常。
内容的提问来源于stack exchange,提问作者LilRazi

