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 {
    }
}

自定义拦截器:
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 {

    }

}

我们这里是需要校验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(myIntercept )
        		//添加需要拦截的接口路径
                .addPathPatterns("intercept/intercept-demo/*");
    }

}

至此,这已经是一个完成可用的拦截器了,可以帮我们统一进行 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 的程序中建议优先使用拦截器,而非过滤器。

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

  • 20
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值