• Spring 拦截器实现请求拦截与参数处理【拦截器(Interceptor)和过滤器(Filter)的区别】


    业务场景:
    项目中需要使用请求头传输一个密文字符串,后端服务获取密文字符串后,进行解密验证,然后执行响应的业务,这里有好几个接口都用需要使用这个密文字符串,如果我们在每个接口中进行校验处理,就显得太笨拙了,那有没有统一的处理方法呢?我想到了使用拦截器 HandlerInterceptor 处理,以下是使用分享。

    什么是拦截器?

    拦截器,顾名思义,就是拦截,对用户请求进行拦截过滤处理。

    拦截器的生效时机?

    • 请求进入 Controller 之前,通过拦截器请求执行相关逻辑(符合我们上面的场景)。
    • Controller 执行之后,只是Controller执行完毕,还没到视图渲染,通过拦截器执行相关逻辑。
    • Controller 执行完毕,请求全部结束,通过拦截器执行相关逻辑。

    HandlerInterceptor 源码:

     package org.springframework.web.servlet;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import org.springframework.lang.Nullable;
    
    public interface 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 {
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    自定义拦截器:
    HandlerInterceptor 接口只给我们定义了方法,具体的业务逻辑需要我们自己去实现,自定义拦截器需要实现HandlerInterceptor ,代码如下:

    package com.study.web.intercept;
    
    import cn.hutool.core.util.StrUtil;
    import com.study.utils.DesECBUtil;
    import com.study.exception.BusinessException;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.codec.binary.Base64;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    
    @Slf4j
    @Component
    public class MyIntercept implements HandlerInterceptor {
    
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            //header 中获取密文
            String code = request.getHeader("code");
            if (StringUtils.isBlank(code)) {
                //为空  不通过
                throw new BusinessException("code 为空,请核实后重试");
            }
            //校验
            String userCode;
            try {
                userCode = DesECBUtil.decryptDES(new String(Base64.decodeBase64(code)), ztmcSecretKey);
            } catch (Exception e) {
                log.error("code 解密失败,失败原因:", e);
                throw new BusinessException("code 解密失败,请联系管理员处理");
            }
            //这个code 有时间戳
            String[] userCodeArr = userCode.split(StrUtil.UNDERLINE);
            //解密出来的是 usercode_时间戳
            String userNo = userCodeArr[0];
            //获取时间戳
            long timestamp = Long.parseLong(userCodeArr[1]);
            //获取当前时间戳
            long currentTimestamp = System.currentTimeMillis() / 1000;
            //当前时间戳-解密时间戳>30分钟 即过期
            log.info("当前时间戳秒:{},解密时间戳秒:{}",currentTimestamp,timestamp);
            if (currentTimestamp - timestamp > 1800) {
                //链接已失效
                throw new BusinessException("链接已失效,请重新打开链接");
            }
            //设置code
            request.setAttribute("code", userNo);
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    我们这里是需要校验header 中的密文字符串是否合法,故要前置校验,所以使用了preHandle 方法。

    配置拦截器

    我们将⾃定义拦截器加⼊ WebMvcConfigurer 的 addInterceptors ⽅法中,并指定需要拦截的接口路径。

    package com.study.web.config;
    
    import com.study.web.intercept.PerformanceReviewIntercept;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    import javax.annotation.Resource;
    
    @Configuration
    public class MyInterceptorConfig implements WebMvcConfigurer {
    
    
        @Resource
        private MyIntercept MyIntercept ;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(performanceReviewIntercept)
            		//添加需要拦截的接口路径
                    .addPathPatterns("intercept/intercept-demo/*");
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    至此,这已经是一个完成可用的拦截器了,可以帮我们统一进行 code 解密验证,就无需去每个 Controller 去处理了。

    拦截器(Interceptor)和过滤器(Filter)的区别?

    • 实现原理不同:拦截器是基于Java反射机制实现,而过滤器是基于函数回调的。
    • 使用范围不同:拦截器是 Spring 的一个组件,由 Spring 容器管理,无需依赖 Tomcat 等容器,可以单独使用,而过滤器实现了 Servlet 接口,导致过滤器的使用要依赖于Tomcat等容器,所以他只能在web程序中使用。
    • 触发时机不同:拦截器是在请求进入servlet后,在进入Controller之前、之后、完毕进行处理的,而过滤器是在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后。
    • 拦截的请求范围不同:拦截器只会对Controller中请求或访问static目录下的资源请求起作用,而过滤器几乎可以对所有进入容器的请求起作用。
    • 拦截器可以获取IOC容器中的各个bean,在拦截器里注入一个bean,可以调用各种业务逻辑,而过滤器就则不可以。

    拦截器(Interceptor)和过滤器(Filter)的相似之处?

    Spring的拦截器与Servlet的Filter有相似之处,比如二者都是AOP编程思想的体现,都能实现权限检查、日志记录等。

    总结:过滤器只能在 Servlet 前后起作用,而拦截器能够到方法前后、异常抛出等,明显拦截器更具备深度,并且拦截器是 Spring 的一个组件,因此拦截器的使用具有更大的使用空间,在Spring 的程序中建议优先使用拦截器,而非过滤器。

    如有不正确的地方请各位指出纠正。

  • 相关阅读:
    一篇文章了解MySQL的group by
    java面向对象之封装-抽象-继承-组合-多态五种概念一网打尽
    java计算机毕业设计学生信息管理系统2021源码+mysql数据库+lw文档+系统+调试部署
    【从零开始学微服务】05.微服务的优势和不足
    CRM客户管理软件对出海企业的帮助与好处
    getcwd()、chdir()和 fchdir()函数的基本使用
    【chromium】windows 获取源码到本地
    UI自动化的适用场景,怎么做?
    【python中级】Pillow包在图像中绘制中文
    【C++ 实战】概论 | 代码风格 | 类
  • 原文地址:https://blog.csdn.net/weixin_42118323/article/details/136723229