Interceptor Handle 执行顺序 preHandle、postHandle、afterCompletion

JWT token验证后,通过 ThreadLocal 进行传值
Spring Boot JWT 用户认证

 preHandle

调用时间:Controller方法处理之前

执行顺序:链式Intercepter情况下,Intercepter按照声明的顺序一个接一个执行

若返回false,则中断执行,注意:不会进入afterCompletion

 

postHandle

调用前提:preHandle返回true

调用时间:Controller方法处理完之后,DispatcherServlet进行视图的渲染之前,也就是说在这个方法中你可以对ModelAndView进行操作

执行顺序:链式Intercepter情况下,Intercepter按照声明的顺序倒着执行

备注:postHandle虽然post打头,但post、get方法都能处理

 

afterCompletion

调用前提:preHandle返回true

调用时间:DispatcherServlet进行视图的渲染之后

多用于清理资源

 

 

AuthInterceptor.java


@Component
public class AuthInterceptor extends HandlerInterceptorAdapter {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private VipSoftConfig VipSoftConfig;

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private StringRedisTemplate redisTemplate;

    public static final String USER_KEY = "userId";


    /**
     * 调用时间:Controller方法处理之前
     * 执行顺序:链式Intercepter情况下,Intercepter按照声明的顺序一个接一个执行
     * 若返回false,则中断执行,注意:不会进入afterCompletion
     *
     * 顾名思义,该方法将在请求处理之前进行调用。SpringMVC中的Interceptor是链式的调用的,在一个应用中或者是在一个请求中可以同时存在多个Interceptor。
     * 每个Interceptor的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor中的preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,
     * 也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是Boolean类型的,当它返回为false时,表示请求结束,后续的Interceptor和Controller都不会再执行;
     * 当返回值为true 时就会继续调用下一个Interceptor的preHandle方法,如果已经是最后一个Interceptor的时候就会是调用当前请求的Controller 方法。
     *
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //如果不是映射到方法的,直接通过
        if (request.getRequestURI().toLowerCase().contains("error")) {
            return true;
        }

        //如果不是映射到方法的,直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        // 忽略带AuthIgnore注解的请求, 不做后续token认证校验
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            AuthIgnore AuthIgnore = handlerMethod.getMethodAnnotation(AuthIgnore.class);
            if (AuthIgnore != null) {
                return true;
            }
        }

        if (HttpMethod.OPTIONS.equals(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
            return true;
        }

        //获取用户凭证
        String token = request.getHeader(jwtUtils.getAccessToken());
        if (StrUtil.isBlank(token)) {
            token = request.getParameter(jwtUtils.getAccessToken());
        }

        //凭证为空
        if (StrUtil.isBlank(token)) {
            throw new CustomException("token 不能为空");
        }

        Claims claims = jwtUtils.getClaimByToken(token);
        if (claims == null || jwtUtils.isTokenExpired(claims.getExpiration())) {
            throw new CustomException(401, "token 失效,请重新登录");
        }
        String[] userinfo = claims.getSubject().split("\\|");
        //判断redis是否存在这个key
        String redisKey = StrUtil.indexedFormat(RedisKey.TOKEN_USER, userinfo[0]);
        String redisValue = redisTemplate.opsForValue().get(redisKey);
        if (!token.equals(redisValue)) {
            throw new CustomException("token 失效,请重新登录");
        }

        //新增ThreadLocal返回用户信息的方式
        TokenEntity tokenEntity = new TokenEntity();
        tokenEntity.setUserId(userinfo[0]);
        tokenEntity.setUserName(userinfo[1]);
        tokenEntity.setAccessToken(token);

//        tokenEntity.setDoctorNo(request.getHeader("DoctorNo"));
//        String doctorName = request.getHeader("DoctorName");
//        if (StrUtil.isNotEmpty(doctorName)) {
//            doctorName = URLDecoder.decode(doctorName, "UTF-8");
//        }
//        tokenEntity.setDoctorName(doctorName);
//        tokenEntity.setDoctorRoleCode(request.getHeader("DoctorRoleCode"));
//        tokenEntity.setHostNo(request.getHeader("HostNo"));

        logger.debug(PojoUtil.pojoToJson(tokenEntity));
        SessionBase.CURRENT_MEMBER.set(tokenEntity);
        return true;
    }


    /**
     * 调用前提:preHandle返回true
     * 调用时间:Controller方法处理完之后,DispatcherServlet进行视图的渲染之前,也就是说在这个方法中你可以对ModelAndView进行操作
     * 执行顺序:链式Intercepter情况下,Intercepter按照声明的顺序倒着执行。
     * 备注:postHandle虽然post打头,但post、get方法都能处理
     *
     * 由preHandle方法的解释我们知道这个方法包括后面要说到的afterCompletion和postHandle方法都只能是在当前所属的Interceptor的preHandle方法的返回值为true时才能被调用。
     * postHandle方法,顾名思义就是在当前请求进行处理之后,也就是Controller方法调用之后执行,但是它会在DispatcherServlet进行视图返回渲染之前被调用,
     * 所以我们可以在这个方法中对Controller处理之后的ModelAndView对象进行操作。postHandle方法被调用的方向跟preHandle是相反的,也就是说先声明的Interceptor的postHandle方法反而会后执行。
     *
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        //logger.error("PostHandler");
    }

    /**
     * 调用前提:preHandle返回true
     * 调用时间:DispatcherServlet进行视图的渲染之后
     * 多用于清理资源
     *
     * 该方法也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。
     * 这个方法的主要作用是用于进行资源清理工作的。
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        SessionBase.CURRENT_MEMBER.remove();  //请求完成后,清除 线程
        //logger.error("afterCompletion");
    }
}

posted @ 2022-01-20 14:44  VipSoft  阅读(298)  评论(0)    收藏  举报