#全局异常处理

现在的开发基本都是前后分离,所有后端返回前端的数据基本都是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 <[email protected]>
 * @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 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 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 <[email protected]>
 * @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){
        Mapmap = 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 <[email protected]>
 * @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;

    }
}

你可能感兴趣的:(#全局异常处理)