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

如何处理JWT SignatureException,返回400而非500状态码?

问题解答

兄弟,你这个判断完全正确!返回500确实不合理,400才是更贴合无效token场景的状态码,甚至用401 Unauthorized会更符合HTTP语义(后面会说),先给你理清楚前因后果,再给你解决办法:

先唠状态码的合理性

500是服务器内部错误,意思是服务器自己处理请求时出了意外,跟客户端没关系;但无效token明显是客户端提交的凭证/参数不合法,属于客户端的问题,用400(Bad Request)完全合理。如果是认证失败的场景,其实401(未授权)更精准——它表示请求需要合法的认证凭证,无效token正好符合这个场景,不过如果你业务上就想定400,也完全没问题。

为啥抛了ResponseStatusException还是返回500?

十有八九是因为你抛出异常的时机不对!比如你在**过滤器(Filter)或者拦截器(Interceptor)**里做token校验并抛出异常,这时候Spring MVC的DispatcherServlet还没接管请求,它的异常处理机制(包括ResponseStatusException的自动处理)根本没生效,这些异常会被Tomcat这类容器直接捕获,返回默认的500错误。

正确返回400的解决方案

分两种常见场景给你说具体做法:

场景1:自己写的Token校验逻辑(没用到Spring Security)

  • 方法一:全局异常捕获
    写一个全局异常处理器,把SignatureException抓过来,手动返回400:

    @ControllerAdvice
    public class GlobalExceptionHandler {
    
        @ExceptionHandler(SignatureException.class)
        public ResponseEntity<ErrorResponse> handleInvalidToken(SignatureException ex) {
            ErrorResponse error = new ErrorResponse("无效的Token", ex.getMessage());
            return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
        }
    
        // 自定义错误响应类
        static class ErrorResponse {
            private String message;
            private String detail;
    
            public ErrorResponse(String message, String detail) {
                this.message = message;
                this.detail = detail;
            }
    
            // getter方法
            public String getMessage() { return message; }
            public String getDetail() { return detail; }
        }
    }
    

    只要是在Spring MVC的处理链里抛出的SignatureException,都会被这个处理器接住,直接返回400和你自定义的响应体。

  • 方法二:过滤器里手动写响应
    如果你的token校验是在Filter里做的,这时候别用ResponseStatusException,直接操作HttpServletResponse就行:

    public class TokenCheckFilter extends OncePerRequestFilter {
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            String token = request.getHeader("Authorization");
            try {
                // 你的token校验逻辑,比如解析JWT签名
                validateToken(token);
                filterChain.doFilter(request, response);
            } catch (SignatureException ex) {
                // 手动设置状态码和响应体
                response.setStatus(HttpStatus.BAD_REQUEST.value());
                response.setContentType("application/json");
                ObjectMapper mapper = new ObjectMapper();
                ErrorResponse error = new ErrorResponse("无效的Token", ex.getMessage());
                response.getWriter().write(mapper.writeValueAsString(error));
            }
        }
    
        // 自定义的token校验方法和ErrorResponse类同上
    }
    

场景2:用Spring Security做JWT认证

如果是Spring Security的JWT校验失败,你可以自定义一个AuthenticationEntryPoint来处理:

@Component
public class CustomAuthEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
        response.setStatus(HttpStatus.BAD_REQUEST.value());
        response.setContentType("application/json");
        ErrorResponse error = new ErrorResponse("无效的Token", authException.getMessage());
        ObjectMapper mapper = new ObjectMapper();
        response.getWriter().write(mapper.writeValueAsString(error));
    }
}

然后在Security配置里把这个EntryPoint加上:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    private CustomAuthEntryPoint customAuthEntryPoint;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .exceptionHandling()
                .authenticationEntryPoint(customAuthEntryPoint) // 配置自定义异常处理
                .and()
            // 其他配置:比如添加JWT过滤器、设置授权规则等
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated());
        return http.build();
    }
}

这样JWT签名验证失败时,就会触发这个EntryPoint,直接返回400状态码。

额外提一句

如果业务场景是“用户未提供有效认证凭证”,用401 Unauthorized会更符合HTTP的标准语义,你只需要把上面代码里的HttpStatus.BAD_REQUEST换成HttpStatus.UNAUTHORIZED就行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 16:42:45