Spring Security JWT认证失败:调用/api/assignments接口返回401错误
问题排查与解决方案
一、核心问题定位
请求携带有效JWT但仍返回401、认证上下文为匿名用户,说明JWT认证流程未正确触发或执行失败,优先排查JWT过滤器的加载、逻辑及Spring Security配置,其次验证@AuthenticationPrincipal的使用合理性。
二、具体排查与修复步骤
1. 检查JWT过滤器是否被Spring Security正确调用
- 验证过滤器顺序:确保JWT过滤器被添加到
UsernamePasswordAuthenticationFilter之前,否则匿名认证会先被触发。配置示例:@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.csrf(csrf -> csrf.disable()) .authorizeHttpRequests(auth -> auth .requestMatchers(HttpMethod.POST, "/api/assignments").authenticated() .anyRequest().authenticated() ) // 关键:将JWT过滤器放在用户名密码认证过滤器之前 .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } - 确认过滤器被实例化:检查过滤器类上是否有
@Component注解,或是否在配置类中手动注入实例。 - 添加执行日志:在过滤器的
doFilterInternal方法开头加日志,确认方法是否被执行。如果未执行,说明过滤器未被Spring Security加载。
2. 检查JWT过滤器逻辑是否正确设置认证上下文
过滤器必须正确解析JWT并将认证信息存入SecurityContextHolder,否则会保持匿名状态。正确逻辑示例:
@Component public class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtTokenProvider jwtTokenProvider; private final UserDetailsService userDetailsService; public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider, UserDetailsService userDetailsService) { this.jwtTokenProvider = jwtTokenProvider; this.userDetailsService = userDetailsService; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token = extractToken(request); // 验证Token有效性并设置认证上下文 if (token != null && jwtTokenProvider.validateToken(token)) { String username = jwtTokenProvider.getUsernameFromToken(token); UserDetails userDetails = userDetailsService.loadUserByUsername(username); UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities() ); authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); // 核心:将认证对象存入上下文 SecurityContextHolder.getContext().setAuthentication(authToken); } filterChain.doFilter(request, response); } private String extractToken(HttpServletRequest request) { String bearerToken = request.getHeader("Authorization"); if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { return bearerToken.substring(7); } return null; } }
- 重点检查:Token解析是否抛出异常未处理、
SecurityContextHolder.getContext().setAuthentication(auth)是否被执行、UserDetails是否正确加载。
3. 验证@AuthenticationPrincipal使用正确性
如果过滤器逻辑正确,但接口仍无法获取用户信息,需检查注解使用:
- 确保注解指定的类型与过滤器中
Authentication的principal类型一致,例如:@PostMapping("/api/assignments") public ResponseEntity<?> createAssignment(@AuthenticationPrincipal UserDetails user) { // 业务逻辑 return ResponseEntity.ok("Success"); } - 如果使用自定义用户对象,需确保
UserDetailsService返回的是该自定义类型,或通过@AuthenticationPrincipal(expression = "#this")指定。
4. 检查Spring Security接口权限配置
- 确认
/api/assignments接口被配置为需要认证:.requestMatchers(HttpMethod.POST, "/api/assignments").authenticated(),而非permitAll()。 - 若使用JWT认证,需关闭CSRF防护:
.csrf(csrf -> csrf.disable()),否则POST请求会因缺少CSRF Token被拦截。
5. 开启Debug日志跟踪认证流程
在application.yml中添加Spring Security debug日志,查看认证全流程:
logging: level: org.springframework.security: DEBUG
重点关注日志中是否有:
- JWT过滤器的执行记录
Set SecurityContextHolder to anonymous SecurityContext的触发时机- Token解析、用户加载的异常信息
内容的提问来源于stack exchange,提问作者Lenroc 777
相关产品推荐
相关产品推荐

