springBoot自定义拦截器
1.应用场景分析
自定义拦截器可以应用于多种场景,可以说是springBoot开发的必备技能
- 日志记录: 可以记录接口的访问信息,访问监控
- 权限检测: 比如在使用jwt作为权限认证的时候可以配置拦截器解析jwt信息
- 多租户信息提取: 关于多租户后期会有相关文章,这里只说一下在请求时可以配置拦截器解析token中的租户信息封装到线程中或者session
2.结构分析
在分析HandlerInterceptorAdapter之前我们先来看一下HandlerInterceptorAdapter的类结构图
HandlerInterceptorAdapter的方法:
3.HandlerInterceptorAdapter中的方法作用
3.1 preHandle:
在方法被调用前执行。在该方法中可以做类似校验的功能。如果返回true,则继续调用下一个拦截器。如果返回false,则中断执行,也就是说我们想调用的方法 不会被执
方法中参数的含义:
HttpServletRequest request——包含所有请求的内容;
HttpServletResponse response——包含所有响应的内容;
Object handler——表示被拦截的请求的目标对象。
3.2 postHandle:
在方法执行后调用。
3.3 afterCompletion:
在整个请求处理完毕后进行回调,也就是说视图渲染完毕或者调用方已经拿到响应。
4. 实例应用
在此我们模拟一个场景,在多租户系统中,在请求到达后端的时候我们可以自定义一个拦截器,拿到此次请求中的jwt的租户信息。
在此我们先模拟封装一个租户信息存储容器:
/**
*
* @author jie
*/
public class BaseContextHandler {
private static final ThreadLocal<Map<String, String>> THREAD_LOCAL = new ThreadLocal<>();
public static void set(String key, Object value) {
Map<String, String> map = getLocalMap();
map.put(key, value == null ? "" : value.toString());
}
public static <T> T get(String key) {
Map<String, String> map = getLocalMap();
return map.get(key);
}
public static void remove() {
if (THREAD_LOCAL != null) {
THREAD_LOCAL.remove();
}
}
}
租户信息拦截器:
/**
* 租户信息解析器
* 用于将请求头中的租户编码和数据库名 封装到当前请求的线程变量中
*
* @author jie
* @date 2019-06-20 22:22
*/
@Slf4j
@AllArgsConstructor
public class TenantContextHandlerInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return super.preHandle(request, response, handler);
}
BaseContextHandler.setTenant("Tenant",this.getHeader(request,"Tenant" ));
return super.preHandle(request, response, handler);
}
private String getHeader(HttpServletRequest request, String name) {
String value = request.getHeader(name);
if (value == null || value .length() == 0) {
value = request.getParameter(name);
}
if (value == null || value .length() == 0) {
return "";
}
return value;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
BaseContextHandler.remove();
super.afterCompletion(request, response, handler, ex);
}
}
这里是在请求到达服务器后从请求头中获取前端设置好的租户信息,取出租户信息存入ThreadLocal中,在程序处理完请求后将ThreadLocal中的租户信息清除。在程序处理中如果要用到租户信息就从线程的ThreadLocal中获取租户信息。
/**
*
* @author jie
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TenantContextHandlerInterceptor())
//所有请求都被拦截
.addPathPatterns("/**")
}
}