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

Java Spring项目JWT认证自定义异常不生效问题

问题根因

@ControllerAdvice 全局异常处理器的生效范围是Spring MVC的DispatcherServlet处理链路,仅能捕获Controller层抛出、以及Controller之前MVC链路内传播的异常。你的OnlineStoreAPIException是在Spring Security过滤器链的JwtAuthenticationFilter.doFilterInternal方法中抛出的,过滤器执行时机早于DispatcherServlet,异常不会传递到全局异常处理器,最终会被Servlet容器默认的异常处理逻辑拦截,返回带完整栈信息的500错误。

解决方案

优先选择在过滤器内部直接处理异常的方案,实现简单、无额外转发逻辑,是JWT认证场景下的通用实践:

  • 在JwtAuthenticationFilter的doFilterInternal方法中,用try-catch块包裹所有JWT校验、认证逻辑,直接捕获OnlineStoreAPIException,手动构造符合格式的响应写入返回流,不再向上抛出异常。

参考实现代码:

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.time.Instant;

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtTokenProvider jwtTokenProvider;
    private final ObjectMapper objectMapper;

    // 构造方法注入依赖
    public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider, ObjectMapper objectMapper) {
        this.jwtTokenProvider = jwtTokenProvider;
        this.objectMapper = objectMapper;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        try {
            String token = extractTokenFromRequest(request);
            if (StringUtils.hasText(token) && jwtTokenProvider.validateToken(token)) {
                Authentication authentication = jwtTokenProvider.getAuthentication(token);
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
            filterChain.doFilter(request, response);
        } catch (OnlineStoreAPIException e) {
            // 重置响应状态、内容类型
            response.setStatus(e.getHttpStatus().value());
            response.setContentType("application/json;charset=UTF-8");
            // 构造和预期格式一致的错误对象
            ErrorDetails error = new ErrorDetails(
                    Instant.now().toString(),
                    e.getHttpStatus().name(),
                    e.getMessage()
            );
            // 将错误对象序列化为JSON写入响应
            response.getWriter().write(objectMapper.writeValueAsString(error));
        }
    }

    private String extractTokenFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}
  • 注意事项:
    • 捕获异常后不要继续调用filterChain.doFilter(),避免后续过滤器执行触发额外异常
    • 项目中如果已经全局配置了ObjectMapper(比如处理了Java时间序列化),直接注入使用即可,不需要手动新建实例
    • 确保ErrorDetails类的字段名、序列化规则和你预期的响应格式匹配

如果希望所有异常统一走全局异常处理器处理,也可以选择异常转发方案:

  1. 在过滤器中捕获到OnlineStoreAPIException后,将异常对象存入request的attribute中,转发到项目内的错误路径(比如/filter-error)
  2. 在Controller层新增对应/filter-error的接口,从request中取出异常对象直接抛出
  3. 此时异常会进入DispatcherServlet处理链路,被@ControllerAdvice正常捕获
    该方案需要额外配置转发规则,避免出现循环转发问题,适合需要统一所有异常处理逻辑的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 06:42:34