业务场景:
项目中需要使用请求头传输一个密文字符串,后端服务获取密文字符串后,进行解密验证,然后执行响应的业务,这里有好几个接口都用需要使用这个密文字符串,如果我们在每个接口中进行校验处理,就显得太笨拙了,那有没有统一的处理方法呢?我想到了使用拦截器 HandlerInterceptor 处理,以下是使用分享。
什么是拦截器?
拦截器,顾名思义,就是拦截,对用户请求进行拦截过滤处理。
拦截器的生效时机?
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(performanceReviewIntercept)
//添加需要拦截的接口路径
.addPathPatterns("intercept/intercept-demo/*");
}
}
至此,这已经是一个完成可用的拦截器了,可以帮我们统一进行 code 解密验证,就无需去每个 Controller 去处理了。
拦截器(Interceptor)和过滤器(Filter)的区别?
拦截器(Interceptor)和过滤器(Filter)的相似之处?
Spring的拦截器与Servlet的Filter有相似之处,比如二者都是AOP编程思想的体现,都能实现权限检查、日志记录等。
总结:过滤器只能在 Servlet 前后起作用,而拦截器能够到方法前后、异常抛出等,明显拦截器更具备深度,并且拦截器是 Spring 的一个组件,因此拦截器的使用具有更大的使用空间,在Spring 的程序中建议优先使用拦截器,而非过滤器。
如有不正确的地方请各位指出纠正。