通过实现HandlerInterceptor接口实现一个拦截器

1. 简介

web应用开发中,拦截器的应用场景非常广泛,主要用于:

  1. 登陆验证:提取request中请求头携带的token信息;
  2. 鉴权:判断该用户是否有权限访问某个资源
  3. 日志记录:记录该handler的入 和 出
  4. 性能监控、通用行为等等一些其它的操作。

2. spring中使用拦截器的方式

spring为我们提供了一个接口:HandlerInterceptor,该接口提供了三个方法:

default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
}

default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}

default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}

通过名字不难理解每个方法其中的意思:
preHandler() 方法是调用我们程序中写的controller之前执行的;
postHander() 方法是controller执行完毕之后,spring会调用该框架;
afterCompletion() 方法是视图渲染完毕之后被执行。

preHander()方法返回true时,表示可以进入咱们开发的handler中,否则不能进入。例如,在preHander中进行鉴权案例:

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    String authHeader = AuthHeaderUtils.getBearerToken();
    String tenantId = AuthHeaderUtils.getTenantId();
    String userId = AuthHeaderUtils.getUserId();

    // 未携带token
    if (StringUtils.isBlank(authHeader)) {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, UNAUTHORIZED);
        return false;
    }
    AuthService authService = SpringUtil.getBean(AuthService.class);
    // 校验token
    TokenInfoDTO tokenInfoDTO = authService.checkToken(authHeader);
    if (Objects.isNull(tokenInfoDTO)) {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, UNAUTHORIZED);
        return false;
    }
    // 是否需要刷新token
    if (tokenInfoDTO.getRefresh()) {
        authHeader = authService.refreshToken(tenantId, userId);
    }
    // 获取用户
    UserDTO user = authService.getUser(authHeader);
    if (Objects.isNull(user)) {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, UNAUTHORIZED);
        return false;
    }
    // 将用户信息set到threadLocal中。
    VBPAppContext.setUserObj(user);
    
    response.setHeader(HEADER_AUTHORIZATION, authHeader);
    return HandlerInterceptor.super.preHandle(request, response, handler);
}

上述代码中通过鉴权的用户将会被set到threadLocal中,虽然ThreadLocal中通过实现WeakReference接口进行弱引用防止内存泄漏,但是未被有效释放的对象仍然会占用内存,因此在一整个请求结束之后,最好及时remove一下,即:在拦截器的afterCompletion()方法中及时调用remove:

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
        throws Exception {
    VBPAppContext.removeUserObj();
}

你可能感兴趣的:(spring,boot)