When using Spring Security to perform jwt verification with a filter, the global exception returns an exception that is not caught in the Filter. hereby record
Reason : Spring Boot
Since global exception handling @RestControllerAdvice
will only catch Controller
exceptions thrown by all layers, the exception class thrown in the filter is unaware.
Solution : transfer the exception to the Controller in the filter
Inject it in the filter HandlerExceptionResolver
and resolver.resolveException()
pass the exception to the Controller:
@Autowired
@Qualifier("handlerExceptionResolver")
private HandlerExceptionResolver resolver;
The complete Filter code is as follows:
@Component
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Resource
private RedisUtil redisUtil;
@Autowired
@Qualifier("handlerExceptionResolver")
private HandlerExceptionResolver resolver;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 获取token
String token = request.getHeader("token");
// 如果请求头中不包含token直接放行
if (StringUtils.isBlank(token)) {
filterChain.doFilter(request, response);
return;
}
// 解析token
String userId = null;
try {
Claims claims = JwtUtil.parseJWT(token);
userId = claims.getSubject();
} catch (BaseException e) {
log.error("非法的token:{}", token);
//throw new BaseException(ResultCode.TOKEN_VALIDATE_LOSE);
resolver.resolveException(request, response, null, new BaseException(ResultCode.TOKEN_VALIDATE_LOSE));
return;
}
// 从redis中获取用户信息
String redisKey = "login:" + userId;
LoginUser loginUser = (LoginUser) redisUtil.get(redisKey);
if (Objects.isNull(loginUser)) {
//throw new BaseException(10007, "用户未登录");
// 交给全局异常处理类处理
resolver.resolveException(request, response, null, new BaseException(10007, "用户未登录"));
return;
}
// 存入SecurityContextHolder
// loginUser.getAuthorities()获取权限信息封装到Authentication
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
// 放行
filterChain.doFilter(request, response);
}
}
Custom exception class:
@EqualsAndHashCode(callSuper = true)
@Data
public class BaseException extends RuntimeException {
private static final long serialVersionUID = 7939259259170410861L;
//错误代码
private Integer code;
// 错误信息
private String message;
public BaseException() {
super();
}
public BaseException(ResultCode resultCode) {
super(resultCode.getMessage());
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
}
public BaseException(Integer code, String message) {
super(message);
this.code = code;
this.message = message;
}
@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
}
Global catch exception class:
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(value = BaseException.class)
public ResponseResult<String> baseExceptionHandler(BaseException e) {
String s = CommonUtils.exceptionInfo(e);
log.error("发生业务异常!异常处在:[{}] , 原因是:[{}]", s, e.getMessage());
return new ResponseResult<>(e.getCode(), e.getMessage());
}
@ExceptionHandler(value = NullPointerException.class)
public ResponseResult<String> exceptionHandler(NullPointerException e) {
String s = CommonUtils.exceptionInfo(e);
log.error("发生空指针异常!异常处在:[{}] , 原因是:[{}]", s, e.getMessage());
return new ResponseResult<>(ResultCode.BODY_NOT_MATCH);
}
@ExceptionHandler(value = Exception.class)
public ResponseResult<String> exceptionHandler(Exception e) {
String s = CommonUtils.exceptionInfo(e);
log.error("未知异常!异常处在:[{}] , 原因是:[{}]", s, e.getMessage());
return new ResponseResult<>(ResultCode.INTERNAL_SERVER_ERROR.getCode(), e.getMessage());
}