You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何找不到UsernamePasswordAuthenticationToken对应的AuthenticationProvider?

问题分析与解决方案

错误根源

登录接口/api/auth/login触发了UsernamePasswordAuthenticationToken类型的认证请求,但你的自定义AuthenticationManager仅注册了JwtAuthenticationProvider——该Provider仅支持JwtAuthenticationToken类型,无法处理用户名密码认证的Token,因此抛出ProviderNotFoundException。

同时你的JwtAuthenticationFilter匹配路径为/login,与实际登录接口/api/auth/login不匹配,导致该Filter不会拦截登录请求(它的作用本应是拦截其他需要JWT校验的接口)。


修复步骤

1. 添加用户名密码认证Provider

创建支持UsernamePasswordAuthenticationToken的Provider,这里使用Spring Security内置的DaoAuthenticationProvider:

@Bean
public AuthenticationProvider usernamePasswordAuthenticationProvider() {
    DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
    provider.setUserDetailsService(byIdUserDetailsService); // 注入你的自定义UserDetailsService
    provider.setPasswordEncoder(passwordEncoder()); // 需提前配置PasswordEncoder Bean
    return provider;
}

2. 更新AuthenticationManager,包含两类Provider

修改authenticationManagerBean方法,同时注册用户名密码和JWT的认证Provider:

@Bean
public AuthenticationManager authenticationManagerBean() {
    return new ProviderManager(
            List.of(
                    usernamePasswordAuthenticationProvider(),
                    new JwtAuthenticationProvider(jwtTokenProvider, byIdUserDetailsService)
            )
    );
}

3. 调整JwtAuthenticationFilter的拦截路径

让JWT过滤器拦截除公开接口外的所有需要认证的接口,而非登录接口:

public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    // 排除公开接口,拦截其他/api路径
    private static final RequestMatcher DEFAULT_REQUEST_MATCHER = new NegatedRequestMatcher(
            new OrRequestMatcher(
                    new AntPathRequestMatcher("/api/auth/**"),
                    new AntPathRequestMatcher("/api/public/**"),
                    new AntPathRequestMatcher("/error")
            )
    );

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(DEFAULT_REQUEST_MATCHER, authenticationManager);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        return this.getAuthenticationManager().authenticate(
                new JwtAuthenticationToken(getJwtFromRequest(request))
        );
    }

    private String getJwtFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}

4. 确保SecurityFilterChain正确传入AuthenticationManager

在配置中注入AuthenticationManager并传递给JwtAuthenticationFilter:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {

    http.csrf(AbstractHttpConfigurer::disable);
    http.cors(AbstractHttpConfigurer::disable);
    http.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
    http.exceptionHandling(exception -> exception
            .authenticationEntryPoint(unauthorizedHandler));
    // 传入AuthenticationManager到Filter
    http.addFilterBefore(new JwtAuthenticationFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class);
    http.authorizeHttpRequests(authorize -> authorize
            .requestMatchers(HttpMethod.OPTIONS).permitAll()
            .requestMatchers("/api/auth/**").permitAll()
            .requestMatchers("/error").permitAll()
            .requestMatchers("/api/public/**").permitAll()
            .anyRequest().authenticated());

    return http.build();
}

5. 验证AuthController的认证逻辑

确保登录接口中使用UsernamePasswordAuthenticationToken进行认证:

@PostMapping("/api/auth/login")
public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest) {
    Authentication authentication = authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(
                    loginRequest.getUsername(),
                    loginRequest.getPassword()
            )
    );

    SecurityContextHolder.getContext().setAuthentication(authentication);
    String jwt = jwtTokenProvider.generateToken(authentication);

    return ResponseEntity.ok(new JwtResponse(jwt));
}

内容的提问来源于stack exchange,提问作者maybesomename

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 08:50:54