场景:自定义拦截器进行accessToken鉴权校验。
1、新建自定义异常类:TokenException,并继承RuntimeException
public class TokenException extends RuntimeException{
private static final long serialVersionUID = 1L;
public TokenException() {
}
public TokenException(String message) {
super(message);
}
}
2、创建全局异常Handler,监控抛出TokenException的场景
@Slf4j
@RestControllerAdvice//使用@ControllerAdvice注解也可以
public class GlobalExceptionHandler {
@ExceptionHandler(ConstraintViolationException.class)
public Resp<String> resolveConstraintViolationException(ConstraintViolationException ex){
Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
if(!CollectionUtils.isEmpty(constraintViolations)){
StringBuilder msgBuilder = new StringBuilder();
for(ConstraintViolation<?> constraintViolation :constraintViolations){
msgBuilder.append(constraintViolation.getMessage()).append(",");
}
String errorMessage = msgBuilder.toString();
if(errorMessage.length()>1){
errorMessage = errorMessage.substring(0,errorMessage.length()-1);
}
log.warn("ConstraintViolationException请求参数校验异常:{}",errorMessage);
return Resp.response(ResponseCode.REQUEST_ERROR, errorMessage);
}
log.warn("ConstraintViolationException请求参数校验异常:{}",ex.getMessage());
return Resp.response(ResponseCode.REQUEST_ERROR, ex.getMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public Resp<String> resolveMethodArgumentNotValidException(MethodArgumentNotValidException ex){
List<ObjectError> objectErrors = ex.getBindingResult().getAllErrors();
if(!CollectionUtils.isEmpty(objectErrors)) {
StringBuilder msgBuilder = new StringBuilder();
for (ObjectError objectError : objectErrors) {
msgBuilder.append(objectError.getDefaultMessage()).append(",");
}
String errorMessage = msgBuilder.toString();
if (errorMessage.length() > 1) {
errorMessage = errorMessage.substring(0, errorMessage.length() - 1);
}
log.warn("MethodArgumentNotValidException请求参数校验异常:{}",errorMessage);
return Resp.response(ResponseCode.REQUEST_ERROR, errorMessage);
}
log.warn("MethodArgumentNotValidException请求参数校验异常:{}",ex.getMessage());
return Resp.response(ResponseCode.REQUEST_ERROR, ex.getMessage());
}
@ExceptionHandler(TokenException.class)
public Resp<String> tokenException(HttpServletRequest req, TokenException e){
log.error("请求:{},拦截器校验不通过",req==null?"":req.getServletPath(),e);
return Resp.response(ResponseCode.REQUEST_ERROR, "accessToken校验失败");//自定义拦截器校验失败抛出的异常
}
@ExceptionHandler(value=Exception.class)
public Resp<String> defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
log.error("请求:{},操作异常{}",req==null?"":req.getServletPath(),e);
return Resp.response(ResponseCode.ERROR, "操作异常");//系统异常,需要后台查看
}
}
3、最后在拦截器的代码逻辑中,若accessToken检验不通过,则抛出TokenException,这时全局异常监控就会返回设置的统一响应码:ResponseCode.REQUEST_ERROR。
@Component
@Slf4j
public class TokenV2Interception extends HandlerInterceptorAdapter {
@Autowired
private RedisUtil redisUtil;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws TokenException {
if (handler instanceof HandlerMethod) {
String accessToken = request.getParameter("accessToken");
log.info("TokenV2Interception拦截路由:"+request.getServletPath()+"|accessToken:{}",accessToken);
if(StringUtils.isBlank(accessToken)){
log.info("拦截器校验不通过:token为null");
throw new TokenException("拦截器校验不通过:token为null");
}
String decode = AuthGenerator.decryptAES(accessToken, Constant.AES_4_ACCESS_TOKEN);
if(StringUtils.isBlank(decode)){
log.info("拦截器校验不通过:decode为null");
throw new TokenException("拦截器校验不通过:decode为null");
}
String appId = decode.substring(decode.indexOf("&")+1);
log.info("appId:{}",appId);
if(StringUtils.isBlank(appId)){
log.info("拦截器校验不通过:appId为空");
throw new TokenException("拦截器校验不通过:appId为空");
}
String serverToken = (String) redisUtil.get(appId);
if(StringUtils.isBlank(serverToken)){
log.info("拦截器校验不通过:服务器token为null");
throw new TokenException("拦截器校验不通过:服务器token为null");
}
if(!serverToken.equalsIgnoreCase(accessToken)){
log.info("拦截器校验不通过:服务器token:{}与accessToken不相等,请求非法",serverToken);
throw new TokenException("拦截器校验不通过:服务器token:{}与accessToken不相等,请求非法");
}
log.info("拦截器校验通过");
return true;
} else {
return true;
}
}
}