关于过滤器中的异常不能被全局异常处理器拦截的问题
场景:
在过滤器中判断token
是否失效,如果失效则需要抛出指定的异常。
问题:
过滤器抛出的异常不能被自定义异常处理器拦截到,返回不了自定义的格式。
解决办法:
- 使用
HandlerExceptionResolver
将异常交给注册的全局异常处理器来处理。(本篇文章的解决办法) - 另写一个过滤器中捕获
chain.doFilter(request, response);
中的异常并放入HttpRequest
中的Attribute
里,然后请求转发到提前写好的controller
里,controller
需要做的就是将异常取出并将异常抛出交给全局异常处理器处理,还需要注意这个过滤器需在过滤器链中靠前。(仅供参考,未经验证)
完整代码如下:
@Component
public class AuthFilter implements Filter {
@Value("${jwt.key}")
private String key;
@Resource
private AuthFilterProperties authFilterProperties;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource(name = "handlerExceptionResolver")
private HandlerExceptionResolver resolver;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse res = (HttpServletResponse) servletResponse;
// 1、过滤请求路径
String uri = req.getRequestURI();
if (authFilterProperties.getExcludedPath().contains(uri)) {
filterChain.doFilter(req, res);
return;
}
// 2、校验token
String token = req.getHeader("Authorization");
String userInfo = null;
try {
if (StrUtil.isEmpty(token)) {
throw new ServiceException(ExceptionEnum.TOKEN_EMPTY);
}
if (!JWTUtil.verify(token, key.getBytes())) {
throw new ServiceException(ExceptionEnum.TOKEN_INVALID);
}
String userId = (String) JWTUtil.parseToken(token).getPayload("userId");
if (StrUtil.isEmpty(userId)) {
throw new ServiceException(ExceptionEnum.TOKEN_INVALID);
}
userInfo = stringRedisTemplate.opsForValue().get(RedisPrefixConstant.ACCESS_TOKEN + userId);
if (StrUtil.isEmpty(userInfo)) {
throw new ServiceException(ExceptionEnum.TOKEN_EXPIRED);
}
} catch (ServiceException e) {
resolver.resolveException(req, res, null, e);
return;
}
// 3、本地线程添加用户信息
req.setAttribute("userInfo", userInfo);
filterChain.doFilter(req, res);
}
}
Tips:
● 全局异常处理器只能处理Spring MVC
里的异常。
● 在过滤器中手动调用 resolver.resolveException(req, res, null, e);
实际上是强制将异常交给了注册的全局异常处理器来处理。这个方法的作用是将异常对象 e
交给 Spring MVC
的异常解析机制来处理,而不是依赖于默认的异常捕获机制。
● 在resolver.resolveException(req, res, null, e)
后面加上return
是为了确保异常处理后,过滤器链不再继续执行。如果没有return
,即使异常被处理,过滤器链仍会继续执行,这可能会导致多次响应,从而在前端看到多次JSON
响应。