使用@Component自定义认证提供者致BasicAuthenticationFilter异常
问题原因分析
你遇到的ProviderNotFoundException本质原因是:
- 启用HttpBasic认证后,
BasicAuthenticationFilter会从请求头的Basic信息生成UsernamePasswordAuthenticationToken,并提交给认证管理器处理。 - 但你的
CustomAuthenticationProvider仅声明支持CustomAuthentication类型,且Spring Security认证管理器中没有其他能处理UsernamePasswordAuthenticationToken的提供者,导致无法匹配到合适的认证逻辑。
另外,当你用@Component注册自定义认证提供者后,Spring Security会自动将其加入认证管理器的提供者列表,但默认的DaoAuthenticationProvider(负责处理UsernamePasswordAuthenticationToken)不会被自动注册,除非你配置了UserDetailsService且没有手动干预认证管理器的构建。
解决方案
根据你的业务需求,有两种处理方式:
方式1:让自定义认证提供者同时支持两种Token类型
如果你的业务允许同一个提供者处理Basic认证和自定义认证,可以修改CustomAuthenticationProvider的supports方法,同时兼容两种Token类型:
@Component public class CustomAuthenticationProvider implements AuthenticationProvider { @Override public boolean supports(Class<?> authentication) { // 同时支持自定义认证Token和Basic认证的UsernamePasswordAuthenticationToken return CustomAuthentication.class.isAssignableFrom(authentication) || UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); } @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { // 分支处理不同类型的Token if (authentication instanceof CustomAuthentication) { // 自定义认证逻辑 } else if (authentication instanceof UsernamePasswordAuthenticationToken) { // Basic认证逻辑:验证用户名密码,比如调用UserDetailsService } // 抛出认证异常或返回认证成功的Authentication对象 } }
方式2:在认证管理器中同时注册两个提供者
如果希望分开处理两种认证逻辑,保持自定义提供者的独立性,可以在Security配置中手动构建认证管理器,同时加入自定义提供者和支持Basic认证的默认提供者:
@Configuration @EnableWebSecurity public class WebSecurityConfig { private final UserDetailsService userDetailsService; private final CustomAuthenticationProvider customAuthenticationProvider; // 构造注入依赖 public WebSecurityConfig(UserDetailsService userDetailsService, CustomAuthenticationProvider customAuthenticationProvider) { this.userDetailsService = userDetailsService; this.customAuthenticationProvider = customAuthenticationProvider; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) .httpBasic(Customizer.withDefaults()) .addFilterBefore(customFilter, BasicAuthenticationFilter.class); return http.build(); } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception { AuthenticationManagerBuilder builder = authConfig.getAuthenticationManagerBuilder(); // 添加自定义认证提供者 builder.authenticationProvider(customAuthenticationProvider); // 添加支持UsernamePasswordAuthenticationToken的DaoAuthenticationProvider DaoAuthenticationProvider daoProvider = new DaoAuthenticationProvider(); daoProvider.setUserDetailsService(userDetailsService); // 配置密码编码器(如果你的用户密码是加密存储的) // daoProvider.setPasswordEncoder(passwordEncoder()); builder.authenticationProvider(daoProvider); return builder.build(); } // 可选:如果需要密码编码器,添加该Bean // @Bean // public PasswordEncoder passwordEncoder() { // return new BCryptPasswordEncoder(); // } }
额外说明
如果你的业务场景不需要Basic认证,仅需自定义认证,那么直接移除配置中的.httpBasic(Customizer.withDefaults())即可,这样就不会触发UsernamePasswordAuthenticationToken的认证流程,也就不会出现这个异常。
内容的提问来源于stack exchange,提问作者The Hawk
相关产品推荐
相关产品推荐

