1. 简介
web应用开发中,拦截器的应用场景非常广泛,主要用于:
- 登陆验证:提取request中请求头携带的token信息;
- 鉴权:判断该用户是否有权限访问某个资源
- 日志记录:记录该handler的入 和 出
- 性能监控、通用行为等等一些其它的操作。
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();
}