拦截器,请求的接口被访问之前,进行拦截然后在之前或之后加入某些操作。拦截是AOP的一种实现策略,拦截器主要用来按照指定规则拒绝请求。

使用场景:

1、Token令牌验证、2、请求数据校验、3、用户权限校验、4、放行指定接口

1. 拦截器的使用

使用拦截器需要进行两步操作,首先需要定义拦截器、然后配置拦截器即可。

1.1 定义拦截器

定义拦截器,需要实现 HandlerInterceptor 接口,该接口中有三个方法: preHandle()、postHandle() 和 afterCompletion()

preHandle() 方法:当某个 url 已经匹配到对应的 Controller 中的某个方法,且在这个方法执行之前。所以 preHandle() 方法可以决定是否将请求放行,这是通过返回值来决定的,返回 true 则放行,返回 false 则不会向后执行。postHandle() 方法:当某个 url 已经匹配到对应的 Controller 中的某个方法,且在执行完了该方法,但是在 DispatcherServlet 视图渲染之前。所以在这个方法中有个 ModelAndView 参数,可以在此做一些修改动作。afterCompletion() 该方法是在整个请求处理完成后(包括视图渲染)执行,这时做一些资源的清理工作,这个方法只有在 preHandle() 被成功执行后并且返回 true 才会被执行。

以上就是对HandlerInterceptor 接口的主要方法的讲解,接下来我们需要自定义拦截器。

package com.bowen.interceptor;import com.bowen.annotation.UnInterception;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.web.method.HandlerMethod;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.lang.reflect.Method;/** *

springboot-study


 * 


自定义拦截器

 * @author : zhang.bw * @date : 2020-07-17 21:37 **/public class MyInterceptor implements HandlerInterceptor {    private static final Logger logger = LoggerFactory.getLogger(MyInterceptor.class);    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        if (handler instanceof HandlerMethod) {            HandlerMethod handlerMethod = (HandlerMethod) handler;            Method method = handlerMethod.getMethod();            String methodName = method.getName();            logger.info("====拦截到了方法:{},在该方法执行之前执行====", methodName);            // 通过方法,可以获取该方法上的自定义注解,然后通过注解来判断该方法是否要被拦截            // @UnInterception 是我们自定义的注解            UnInterception unInterception = method.getAnnotation(UnInterception.class);            if (null != unInterception) {                return true;            }            // 判断用户有没有登陆,一般登陆之后的用户都有一个对应的token            String token = request.getParameter("token");            if (null == token || "".equals(token)) {                logger.info("用户未登录,没有权限执行……请登录");                return false;            }        }        // 返回true才会继续执行,返回false则取消当前请求        return true;    }    @Override    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {        logger.info("执行完方法之后执行(Controller方法调用之后),但是此时还没进行视图渲染");    }    @Override    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {        logger.info("整个请求都处理完成,DispatcherServlet也渲染了对应的视图,在这里可以做清理事项");    }}

经过上述步骤,我们已完成拦截器的定义。

1.2 配置拦截器

在 Spring Boot 2.0 之前,我们都是直接继承 WebMvcConfigurerAdapter 类,然后重写 addInterceptors 方法来实现拦截器的配置。但是在 Spring Boot 2.0 之后,该方法已经很少用了,取而代之的是 WebMvcConfigurationSupport 类,如下:

@Configurationpublic class MyInterceptorConfig extends WebMvcConfigurationSupport {    @Override    protected void addInterceptors(InterceptorRegistry registry) {        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");        super.addInterceptors(registry);    }}

我们配置了一个拦截器,在MyInterceptorConfig中我们重写了addInterceptors方法,将定义的拦截器MyInterceptor加进去,addPathPatterns 方法是添加要拦截的请求,这里我们拦截所有的请求

新建一个InterceptorController测试一下,启动项目,在浏览器中输入 http://localhost:8080/interceptor/test1?token=2020

@Controller@RequestMapping("/interceptor")public class InterceptorController {    @RequestMapping("/test1")    public String test1() {        return "hello";    }}

测试结果:可以看出拦截器已经生效,并能看出其执行顺序。




springboot 拦截器 重新设置 requestBody springboot拦截器拦截所有请求_静态资源

拦截器测试



1.3 静态资源被拦截问题处理

上述配置有一个问题,那就是静态资源被拦截了。

我们可以在 resources/static/ 目录下放置一个图片ymtd.png,然后请求静态资源,发现被拦截了。

所以对于 WebMvcConfigurationSupport 而言,它会导致默认的静态资源被拦截,这就需要我们手动将静态资源放开。

怎么放开?需要再重写一个方法:addResourceHandlers,将静态资源放开:

/** * 用来指定静态资源不被拦截,否则继承WebMvcConfigurationSupport这种方式会导致静态资源无法直接访问 * @param registry */@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {    registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");    super.addResourceHandlers(registry);}

访问静态资源,浏览器输入:http://localhost:8080/ymtd.png




springboot 拦截器 重新设置 requestBody springboot拦截器拦截所有请求_ide_02

拦截器测试



配置完成后,重启项目,静态资源也可以正常访问了。

配置拦截器还有一种方式,那就是直接实现 WebMvcConfigurer 接口,然后重写 addInterceptors 方法,将自定义的拦截器添加进去即可,如下:

@Configurationpublic class MyInterceptorConfig implements WebMvcConfigurer {    @Override    public void addInterceptors(InterceptorRegistry registry) {        // 实现WebMvcConfigurer不会导致静态资源被拦截        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");    }}

实现 WebMvcConfigure 接口的话,不会拦截 Spring Boot 默认的静态资源。

继承 WebMvcConfigurationSupport 类的方式可以用在前后端分离的项目中,后台不需要访问静态资源(就不需要放开静态资源了);实现 WebMvcConfigure 接口的方式可以用在非前后端分离的项目中,因为需要读取一些图片、css、js文件等。

2. 拦截器使用例子

2.1 判断用户是否登录

一般用户登录功能我们可以这么做,要么往 session 中写一个 user,要么针对每个 user 生成一个 token,第二种要更好一点,那么针对第二种方式,如果用户登录成功了,每次请求的时候都会带上该用户的 token,如果未登录,则没有该 token,服务端可以检测这个 token 参数的有无来判断用户有没有登录,从而实现拦截功能。我们改造一下 preHandle 方法,如下:

在 MyInterceptor 的 preHandle方法中加入以下判断// 判断用户有没有登陆,一般登陆之后的用户都有一个对应的tokenString token = request.getParameter("token");if (null == token || "".equals(token)) {    logger.info("用户未登录,没有权限执行……请登录");    return false;}

重启项目,在浏览器中输入 localhost:8080/interceptor/test1 后查看控制台日志,发现被拦截,如果在浏览器中输入 localhost:8080/interceptor/test1?token=2020 即可正常访问。

2.2 取消拦截操作

如果要拦截所有 /user 开头的 url 请求的话,需要在拦截器配置中添加这个前缀,但是在实际项目中,可能会有这种场景出现:某个请求也是 /user 开头的,但是不能拦截,比如 /user/login 等等,这样的话又需要去配置。那么,可不可以做成一个类似于开关的东西,哪里不需要拦截,我就在哪里弄个开关上去,做成这种灵活的可插拔的效果呢?

是可以的,我们可以定义一个注解,该注解专门用来取消拦截操作,如果某个 Controller 中的方法我们不需要拦截掉,即可在该方法上加上我们自定义的注解即可,下面先定义一个注解:

/** *

springboot-study


 *  
 
该注解用来指定某个方法不用拦截
 
  * @author : zhang.bw * @date : 2020-07-17 21:37 **/@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface UnInterception {}

然后在 Controller 中的某个方法上添加该注解,在拦截器处理方法中添加该注解取消拦截的逻辑,如下:

在 MyInterceptor 的 preHandle方法中加入以下判断// 通过方法,可以获取该方法上的自定义注解,然后通过注解来判断该方法是否要被拦截// @UnInterception 是我们自定义的注解UnInterception unInterception = method.getAnnotation(UnInterception.class);if (null != unInterception) {    return true;}

Controller 中的方法代码可以参见源码,重启项目在浏览器中输入 http://localhost:8080/interceptor/test2?token=123 测试一下,可以看出,加了该注解的方法不会被拦截。

3. 总结

本节课程主要介绍了 Spring Boot 中拦截器的使用,从拦截器的创建、配置,到拦截器对静态资源的影响,都做了详细的分析。Spring Boot 2.0 之后拦截器的配置支持两种方式,可以根据实际情况选择不同的配置方式。通过本节课程的学习,相信对Spring Boot拦截器的使用有了比较深刻的印象。

4. 源码获取