#全局异常处理
2020-09-27 本文已影响0人
努力耕耘少问收获
现在的开发基本都是前后分离,所有后端返回前端的数据基本都是json格式,但是有时候对引用第三方框架的时候会抛出异常,但又不是我们想要的格式比如拦截器异常、JWT异常等,比如下面的
①HandlerInterceptor
package com.hytc.interceptor;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.hytc.annotation.PassToken;
import com.hytc.annotation.UserLoginToken;
import com.hytc.exception.FailException;
import com.hytc.exception.JWTException;
import com.hytc.response.RestResponse;
import com.hytc.service.LoginService;
import com.hytc.util.ToolUtil;
import com.hytc.vo.User;
import com.sun.javafx.font.FontStrikeDesc;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Map;
/**
* @ClassName: AuthenticationInterceptor
* @Description:TODO(实现一个拦截器就需要实现HandlerInterceptor接口,目的是拦截器去获取token并验证token)
*
* @Author: BYP <502955177@qq.com>
* @Date: 2020/4/17 16:51
* @Copyright: 2019 www.tydic.com Inc. All rights reserved.
* 注意:本内容仅限于弘毅天承信息技术股份有限公司内部传阅,禁止外泄以及用于其他的商业目
*/
@Slf4j
public class AuthenticationInterceptor implements HandlerInterceptor {
@Autowired
private LoginService loginService;
@Autowired
private RedisTemplate<Object,Object> redisTemplate;
/**
*
* @param httpServletRequest
* @param httpServletResponse
* @param object
* @return
* 预处理回调方法,实现处理器的预处理,第三个参数为响应的处理器,自定义Controller,返回值为true表示继续流程(如调用下一个拦截器或处理器)或者接着执行postHandle()和afterCompletion();
* false表示流程中断,不会继续调用其他的拦截器或处理器,中断执行。
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token
// 如果不是映射到方法直接通过
if(!(object instanceof HandlerMethod)){
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) object;
Method method = handlerMethod.getMethod();
//检查是否有passtoken注释,有则跳过认证
if(method.isAnnotationPresent(PassToken.class)){
PassToken passToken = method.getAnnotation(PassToken.class);
if(passToken.required()){
return true;
}
}
//检查有没有需要用户权限的注解
if (method.isAnnotationPresent(UserLoginToken.class)) {
UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
if (userLoginToken.required()) {
// 执行认证
if (token == null) {
throw new JWTException("205","无token,请重新登录");
}
// 获取 token 中的 userGuid
String userGuid;
try{
userGuid = JWT.decode(token).getAudience().get(0);
}catch (JWTDecodeException j){
throw new JWTException("300","密匙非法,请重新登陆系统");
}
User user = loginService.findUserByUserGuid(userGuid);
if (user == null) {
throw new JWTException("201","用户不存在,请重新登录");
}
//==============================2020-07-24 13:52:45===========================================
String ip= ToolUtil.getClientIp(httpServletRequest);
if("0.0.0.0".equals(ip) || "0.0.0.0.0.0.1".equals(ip) || "localhost".equals(ip) || "127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip)) {
ip = "127.0.0.1";
}
Map<String,String> browserMap = ToolUtil.getOsAndBrowserInfo(httpServletRequest);
String browser = browserMap.get("browser");
if(!"".equals(user.getRemoteAddr()) || !"".equals(user.getBrowser())){
if(!user.getRemoteAddr().equals(ip)){
throw new JWTException("206","登陆失败,原因:对不起,系统检测到您在此账号在另一台设备上已经登录!如果您是由于上次没有正常退出系统,请等待3分钟后重新登陆!");
}else if(!user.getBrowser().equals(browser)){
throw new JWTException("206","登陆失败,原因:对不起,系统检测到此账号在在同一设备的另一浏览器中登录!如果您是由于上次没有正常退出系统,请等待3分钟后重新登陆!");
}
}
/*else if(!"".equals(user.getBrowser())){
if(!user.getBrowser().equals(browser)){
throw new JWTException("202","登陆失败,原因:对不起,系统检测到此账号在在同一设备的另一浏览器中登录!如果您是由于上次没有正常退出系统,请等待3分钟后重新登陆!");
}
}*/
/*if(user.get登录次数()>=1){
throw new JWTException("202","系统检测到您同一账号在另外一处设备登录");
}*/
//==============================2020-07-24 13:52:45===========================================
// 验证 token
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
try {
jwtVerifier.verify(token);
Object res = redisTemplate.opsForValue().get(user.getUsername());
/*if(res==null){
throw new JWTException("206","密匙过期,请重新登陆系统");
}
log.info(res.toString());*/
} catch (JWTVerificationException e) {
throw new JWTException("300","密匙非法,请重新登陆系统");
}
return true;
}
}
return true;
}
/**
* @param request
* @param response
* @param handler
* @param modelAndView
* 后处理回调方法,实现处理器的后处理(DispatcherServlet进行视图返回渲染之前进行调用),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,
* modelAndView也可能为null。
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
/**
* @param request
* @param response
* @param handler
* @param ex
* 整个请求处理完毕回调方法,该方法也是需要当前对应的Interceptor的preHandle()的返回值为true时才会执行,也就是在DispatcherServlet渲染了对应的视图之后执行。
* 用于进行资源清理。整个请求处理完毕回调方法。如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
针对这种情况我们需要手写异常进行按照自己的格式返回
首先我们对spring全局异常有所了解,用的注解是@RestControllerAdvice
package com.hytc.mall.hytcmall.exception;
import com.google.common.collect.Maps;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* @ClassName: GlobalExceptionHandler
* @Description:全局异常处理
* @Author: BYP <502955177@qq.com>
* @Date: 2020/9/27 10:03
* @Copyright: 2019 www.tydic.com Inc. All rights reserved.
* 注意:本内容仅限于弘毅天承信息技术股份有限公司内部传阅,禁止外泄以及用于其他的商业目
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(RequestLimitException.class)
public Map defaultExceptionHandler(HttpServletRequest request,Exception e){
Map<String,Object>map = Maps.newConcurrentMap();
if(e instanceof RequestLimitException){
RequestLimitException limit = (RequestLimitException) e;
map.put("code", limit.getCode());
map.put("msg", limit.getMsg());
}else{
map.put("code", -1);
map.put("msg", "系统异常");
}
//未知错误
return map;
}
}
③编写自定义格式的返回状态码
package com.hytc.mall.hytcmall.exception;
import lombok.Data;
/**
* @ClassName: RequestLimit
* @Description:
* @Author: BYP <502955177@qq.com>
* @Date: 2020/9/27 9:59
* @Copyright: 2019 www.tydic.com Inc. All rights reserved.
* 注意:本内容仅限于弘毅天承信息技术股份有限公司内部传阅,禁止外泄以及用于其他的商业目
*/
@Data
public class RequestLimitException extends RuntimeException{
/*错误码*/
private String code;
/*错误提示*/
private String msg;
public RequestLimitException(){
}
public RequestLimitException(String code,String msg){
this.code = code;
this.msg = msg;
}
}