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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 02:44:49