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

Spring Security JWT无客户端认证问题:基于1.0.9.RELEASE的求助

解决Spring Security JWT "There is no client authentication" 错误

我之前也碰到过这个问题,结合你使用的spring-security-jwt-1.0.9.RELEASE(对应Spring Security 4.x系列),这个报错核心是你的Spring Security过滤链里缺少了处理JWT客户端认证的过滤器,同时CORS配置也需要和Security过滤链配合调整,下面是具体的修复步骤:

1. 完善Spring Security配置类,添加JWT认证过滤器

首先要在Security配置类中,把JWT相关的认证过滤器加入到过滤链中。如果是使用客户端凭据模式,需配置ClientCredentialsTokenEndpointFilter;如果是Bearer Token模式,可以自定义JWT解析过滤器,示例代码如下:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private UserDetailsService userDetailsService;

    // 配置认证管理器
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    // 配置HTTP安全规则
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                // 优先处理CORS
                .cors()
                .and()
                // 放行登录等无需认证的接口
                .authorizeRequests()
                .antMatchers("/auth/**").permitAll()
                .anyRequest().authenticated()
                .and()
                // 将JWT过滤器添加到用户名密码认证过滤器之前
                .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    // 自定义JWT认证过滤器
    public JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
        JwtAuthenticationFilter filter = new JwtAuthenticationFilter();
        filter.setAuthenticationManager(authenticationManager());
        filter.setFilterProcessesUrl("/auth/login"); // 登录接口路径
        return filter;
    }

    // 密码编码器
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

2. 实现JWT认证过滤器

编写自定义的JwtAuthenticationFilter来处理JWT的生成、解析和认证逻辑:

public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final JwtTokenUtil jwtTokenUtil;
    private final UserDetailsService userDetailsService;

    public JwtAuthenticationFilter() {
        this.jwtTokenUtil = new JwtTokenUtil();
        this.userDetailsService = new CustomUserDetailsService();
    }

    // 处理登录请求,生成JWT返回给客户端
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        String username = obtainUsername(request);
        String password = obtainPassword(request);

        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
        setDetails(request, authRequest);
        return this.getAuthenticationManager().authenticate(authRequest);
    }

    // 认证成功后生成并返回JWT
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        UserDetails userDetails = (UserDetails) authResult.getPrincipal();
        String token = jwtTokenUtil.generateToken(userDetails);
        response.addHeader("Authorization", "Bearer " + token);
        super.successfulAuthentication(request, response, chain, authResult);
    }

    // 处理后续请求的JWT解析与身份校验
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        String token = request.getHeader("Authorization");
        if (token != null && token.startsWith("Bearer ")) {
            token = token.substring(7);
            String username = jwtTokenUtil.getUsernameFromToken(token);
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                if (jwtTokenUtil.validateToken(token, userDetails)) {
                    UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authToken);
                }
            }
        }
        chain.doFilter(request, response);
    }
}

3. 修正CORS过滤器配置

你的CORSFilter需要确保在Spring Security过滤器之前执行,通过@Order设置最高优先级,同时补全完整的CORS响应头:

@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
public class CORSFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        response.setHeader("Access-Control-Allow-Origin", "*"); // 生产环境建议指定具体域名
        response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, Accept");
        response.setHeader("Access-Control-Max-Age", "3600");

        // 直接放行OPTIONS预检请求
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
        } else {
            filterChain.doFilter(request, response);
        }
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void destroy() {}
}

4. 确保依赖版本兼容

spring-security-jwt-1.0.9.RELEASE是基于Spring Security 4.x开发的,所以你的项目中Spring Security的版本要对应(比如4.2.x系列),避免版本冲突导致过滤器无法正常加载。

关键注意点

  • JWT过滤器必须被正确添加到Spring Security过滤链中,顺序不能出错
  • CORS过滤器的优先级要高于Security过滤器,否则OPTIONS预检请求会被Security拦截
  • 如果使用OAuth2客户端认证模式,还需要配置ClientDetailsService和对应的客户端信息

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:40:00