1. 拦截器

1.1. 概念

Spring MVC中的拦截器(Interceptor)类似于servlet中的过滤器(Filter),它主要用于在特定的时间点,拦截用户请求并作出相应的处理。

拦截器和过滤器的实现方式不同。拦截器是使用动态代理,即反射机制来实现的。而过滤器则是依赖于servlet容器,通过函数回调的方式实现的。

1.2.拦截器处理流程

拦截器主要用来在三个时间点拦截用户的请求,对请求进行判断和处理。

这三个时间点分别是:preHandler、postHandler、afterCompletion

  1. preHandler

    返回值为boolean,在请求进入controller处理之前就拦截,通过返回值控制这个请求能否进入controller。

    在这个方法中,我们一般进行身份认证的相关操作,认证通过就对请求放行,反之则不允许进入,并直接将请求返回。

    需要注意的是,我们可以通过对这个方法参数中的response进行处理,来决定如果拦截以后,返回什么信息给请求方。

  2. postHandle

    无返回值。这个方法在controller执行结束并返回的时候拦截。

    我们可以在这个方法中获取controller返回的ModulAndView对象,意味着我们可以对controller返回的内容进行再包装,比如添加一些公用的参数等数据。

  3. afterCompletion

    无返回值。这个方法是在客户端收到了返回的响应,并完成了视图的解析以后,再调用拦截器的afterCompetion方法。

    需要注意的是,现在我们已经很少直接使用视图渲染页面了,但是请求正常结束,或被拦截返回,都会被afterCompletion拦截到。

    这个方法的返回值中包含了异常信息,所以我们可以在这里做统一异常处理,或统一日志处理。

另外,如果有多个拦截器,拦截器之间出现了层叠关系,那么自然就会存在拦截的顺序。顺序如下图所示:

注意,只有afterCompletion是逆序执行的。

调用顺序

2. 代码实现


实现思路:

  1. 编写一个拦截器,继承HandlerInterceptor接口
  2. 编写(或修改)SpringMVC配置类,注册我们写的拦截器

目录结构如下

config
 └─ WebMVCConfig            # 配置springMVC框架信息

interceptor                 
 └─ XXXInterceptor          # 具体的拦截器

我们以一个登录身份验证的拦截为例子

简单来说,如果用户访问除了登录和注册的页面,就需要检验用户的请求是否包含token并校验,token合法才允许请求通过,否则将请求直接退回。

LoginInterceptor.java

验证登录拦截器类,内部对拦截到的内容进行具体处理,决定是否放行

/**
 * 登录验证拦截器
 * @Author Mosfield
 * @Date 2023-6-7
 */
@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Autowired
    JwtConfig jwtConfig;

    /**
     * 检查用户是否登录
     * return true => 放行逻辑 => 可以继续走三层
     * return false => 拦截 =>到此为止了
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // 从请求头里获取token
        String token = request.getHeader("token");
        try {
            // ======================== 具体的验证的逻辑 (jwt 验证) ========================
            Claims claims = JwtUtils.parseToken(token, jwtConfig.getSecureKey());
            // 登录成功
            return true;
        } catch (Exception e) {
            //被拦截了,我们需要给提示(请登陆后继续操作)
            ObjectMapper objectMapper = new ObjectMapper();
            String jsonStr = objectMapper.writeValueAsString(Result.error("请登陆后重试"));
            //把json格式的数据写给前端
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write(jsonStr);
            return false;
        }
    }
}

WebMVCConfig.java

配置SpringMVC框架信息,注册拦截器

/**
 * 对springMVC框架进行控制
 * @Author Mosfield
 */
@Configuration // 标记当前的类是一个配置类,它单独加载
public class WebMVCConfig implements WebMvcConfigurer {

    // 登录拦截器对象
    @Autowired
    LoginInterceptor loginInterceptor;

    /**
     * 对拦截器进行配置
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        // 需要放行的资源列表
        ArrayList<String> acl = new ArrayList();
        acl.add("/login");
        acl.add("/register");

        // 注册拦截器,并指定拦截器拦截哪些路径,放过哪些路径
        registry.addInterceptor(loginInterceptor)
                // 设置拦截路径 => /** 指全部拦截
                .addPathPatterns("/**")
                // 设置放行路径
                .excludePathPatterns(acl);
    }
}
如人饮水,冷暖自知。
最后更新于 2023-08-05