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
相关产品推荐
相关产品推荐

