一、什么是拦截器
拦截器是spring aop思想的一种体现和运用,底层通过动态代理模式完成;拦截器不依赖于servlet容器,依赖于web框架,由于拦截器是基于web框架的调用,因此可以使用spring的依赖注入(DI)获取IOC容器中的各个bean;可以用来拦截一些非法的Action请求,或者是对Action请求进行一些预处理和请求后的一些处理等,使得我们业务更加符合实际需求,当然也可以在一定程度上保证系统的安全性。
二、拦截器的使用场景是什么
1.Token令牌的验证:验证token是否过期,token数据是否被篡改
2.日志记录:记录请求信息的日志,以便进行信息监控、信息统计等
3.权限检查:如用户登录校验,如果用户登录则进行Action方法,否则直接返回登录页面
4.性能监控:如检测方法的执行时间,在进入控制器之前记录开始时间,在处理完成后记录结束时间,得到请求处理总时间
三、spring boot 2.X实现自定义拦截器的方法
spring boot 2.X和1.X版本在定义拦截器时有两点不同:
1.spring boot 2.X和1.X在配置类上的定义方式不同
1.X和2.X在拦截器的实现类上都是实现HandlerInterceptor接口,这点两者没有区别;但是在配置方式上有区别,1.X版本在 自定义拦截器配置类上是继承WebMvcConfigurerAdapter类,WebMvcConfigurerAdapter实际上是 WebMvcConfigurer接口的空实现类;
在2.X版本中对应的是Spring5.x,而Spring5.0以后WebMvcConfigurerAdapter已经过时了,2.X版本的拦截器的配置类是实现WebMvcConfigurer接口,然后重写接口中的方法,这也是官方推荐使用的。在2.X版本中继续使用WebMvcConfigurerAdapter类也不是不可以,只是不推荐而已;
2.spring boot 2.X和1.X在静态资源访问上不同
1.X的resources/static目录下的静态资源可以直接访问,并且访问的时候不需要在路径上加static;如果有自定义的拦截器进 行拦截时,请求静态资源的路径也不会被拦截;
2.X如果有自定义的拦截器时,访问静态资源的文件就会被拦截,解决方法是在拦截器的配置类上配置静态资源路径,使得拦截器不在进行拦截;
2.X版本自定义拦截器并实现HandlerInterceptor接口,代码如下:
public class TokenInterceptor implements HandlerInterceptor
{
/*
* 进入controller层之前拦截请求
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("************TokenInterceptor preHandle executed**********");
System.out.println("getContextPath:" + request.getContextPath());
System.out.println("getServletPath:" + request.getServletPath());
System.out.println("getRequestURI:" + request.getRequestURI());
System.out.println("getRequestURL:" + request.getRequestURL());
// 返回true,postHandler和afterCompletion方法才能执行
// 否则false为拒绝执行,起到拦截器控制作用
return true;
}
/*
* 处理请求完成后视图渲染之前的处理操作,但只有preHandle方法返回true的时候才会执行
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
System.out.println("************TokenInterceptor postHandle executed**********");
}
/*
* 视图渲染之后的操作,同样需要preHandle返回true
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("************TokenInterceptor afterCompletion executed**********");
}
}
- preHandler:业务处理器处理请求之前被调用,对用户的request进行处理,若返回值为true,则继续调用后续的拦截器和目标方法;若返回值为false,则终止请求;这里可以加上登录校验,权限拦截等;
- postHandler:Controller执行以后,但是未返回视图前调用该方法,这里可以对模型数据在返回用户之前进行加工处理;
- afterCompletion:Controller执行以后且返回视图后调用该方法,可以得到Controller时的异常信息,这里可以记录操作日志,资源清理等。
2.X版本自定义配置类并实现WebMvcConfigurer接口,在该配置类中对2.X版本静态资源不能访问的问题进行修复,代码如下:
@Configuration
public class WebConfig implements WebMvcConfigurer
{
@Autowired
private InterceptorPathBean interceptorPathBean;
// 以下WebMvcConfigurerAdapter 比较常用的重写接口
// /** 解决跨域问题 **/
// public void addCorsMappings(CorsRegistry registry);
// /** 添加拦截器 **/
// void addInterceptors(InterceptorRegistry registry);
// /** 配置视图解析器 **/
// void configureViewResolvers(ViewResolverRegistry registry);
// /** 配置内容裁决的一些选项 **/
// void configureContentNegotiation(ContentNegotiationConfigurer configurer);
// /** 视图跳转控制器 **/
// void addViewControllers(ViewControllerRegistry registry);
// /** 静态资源处理 **/
// void addResourceHandlers(ResourceHandlerRegistry registry);
// /** 默认静态资源处理器 **/
// void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer);
/**
* 表示这些配置的表示静态文件所处路径, 不用拦截
* 注意:这段代码同时也指定了项目静态资源的访问路径,所以可以不用在application.yml文件中配置:spring.mvc.static-path-pattern=/static/**
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
registry.addResourceHandler("/templates/**")
.addResourceLocations("classpath:/templates/");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor())
// 设置需要拦截的路径
// 这里add"/**"(拦截所有请求),下面的exclude才起作用,且不管controller层是否能匹配客户端请求,拦截器都起作用拦截
.addPathPatterns(interceptorPathBean.getInclude()) // 拦截
.excludePathPatterns(interceptorPathBean.getExclude()); // 不拦截
}
@Bean
public TokenInterceptor tokenInterceptor() {
return new TokenInterceptor();
}
}
我这里是将拦截器需要拦截的路径和不需要拦截的路径在yml文件中进行了配置,然后注入InterceptorPathBean,获取需要拦截 的和不需要拦截的路径;
在该配置类中重新了addResourceHandlers(ResourceHandlerRegistry registry)方法来实现静态资源的访问;
四、spring boot 注册多个拦截器以及拦截器的执行顺序
如果需要同时注册多个拦截器,只需要在拦截器配置类中的addInterceptors(InterceptorRegistry registry)方法中依次注册就行,代码如下:
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor())
// 设置需要拦截的路径
// 这里add"/**"(拦截所有请求),下面的exclude才起作用,且不管controller层是否能匹配客户端请求,拦截器都起作用拦截
.addPathPatterns(interceptorPathBean.getInclude()) // 拦截
.excludePathPatterns(interceptorPathBean.getExclude()); // 不拦截
registry.addInterceptor(loginInterceptor())
.addPathPatterns(interceptorPathBean.getInclude()) // 拦截
.excludePathPatterns(interceptorPathBean.getExclude()); // 不拦截
}
@Bean
public TokenInterceptor tokenInterceptor() {
return new TokenInterceptor();
}
@Bean
public LoginInterceptor loginInterceptor() {
return new LoginInterceptor();
}
多个拦截器注册时,执行顺序跟addInterceptor()这个方法添加拦截器的先后顺序有关,preHandle这个方法是先添加,先执行。 postHandle 和 afterCompletion 这两个方法是先添加后执行。