拦截器的使用
拦截器(Interceptor)是一种在应用程序中用于干预、修改或拦截请求和响应的组件,是AOP 编程的一种实践,和过滤器一样都是一种具体的AOP实现。它可以在请求被发送到目标处理程序之前或之后,对请求进行预处理或对响应进行后处理。
拦截器通常用于以下目的:
认证和授权:拦截器可以对请求进行身份验证和权限检查,确保只有经过认证和授权的用户才能访问特定的资源或执行特定的操作。
日志记录和性能监控:拦截器可以用于记录请求和响应的日志,以便进行故障排查、性能监控和分析。
请求转发和重定向:拦截器可以根据特定的条件,对请求进行转发或重定向至不同的处理程序或页面。
数据转换和格式化:拦截器可以对请求和响应的数据进行转换、格式化或验证,以满足特定的需求或规范。
在很多框架和应用程序中都有拦截器的概念,比如在Java中的Spring MVC框架中,可以使用拦截器来拦截和处理HTTP请求;在网络安全领域,防火墙和入侵检测系统也可以使用拦截器来拦截和过滤恶意流量。拦截器在应用程序中起到了很重要的作用,增强了系统的安全性、可靠性和可控性。
1.拦截器的编写方法
拦截器的编写比较简单,一般是根据自己的不同需要继承不同的拦截器基类。大致分为两类,一类是基于业务判断服务的拦截器,比如:日志服务,权限认证,这种拦截器一般继承自 HandlerInterceptor类;另一类是和配置有关的拦截器,一般继承自 WebMvcConfigurer 。其中第一类最多,需要重写 preHandle 和 postHandle 方法,其中 preHandle 方式用于请求执行前的时候,当preHandle 返回 true 才能进行下一个方法,而 postHandle 是作用于请求结束前。afterCompletion 是视图显示完成后才会执行。
第一类,基于 HandlerNterceptor 类的拦截器 ,用于日志服务:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;@Component
public class LoggerInterceptor extends HandlerInterceptorAdapter {//生成日志类private Logger logger = LoggerFactory.getLogger(LoggerInterceptor.class);@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HandlerMethod handlerMethod = (HandlerMethod) handler;// Returns the name of the HTTP method with which this request was made, for example, GET, POST, or PUT.Method method = handlerMethod.getMethod();//得到方法信息//从当前方法中获取一个日志注解对象LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);if (logAnnotation != null){long startTime = System.currentTimeMillis();request.setAttribute("startTime",startTime);//设置属性logger.info("enter"+method.getName()+"method cost time is:"+startTime+" ms");}return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();//得到方法信息LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);if (logAnnotation != null){long endTime = System.currentTimeMillis();long startTime = (long) request.getAttribute("startTime");long periodTime = endTime- startTime;logger.info("Leave "+method.getName()+"method time is:"+endTime+" ms");logger.info("On "+method.getName()+"method cost time is:"+periodTime+" ms");}}
}
将分别计算并输出进入某个方法和离开某个方法的时间。
声明注解 :在需要使用该服务的方法上加上该注解即可使用
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface LogAnnotation {String message() default "666";String[] params() default {};}
注册配置拦截器:实现WebMvcConfigurer接口,并重写addInterceptors方法
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate LoggerInterceptor loggerInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loggerInterceptor).addPathPatterns("/**").excludePathPatterns("/ls");//不拦截的路径}
}
2.邮箱有效性验证
常见的邮箱如 qq,网易,地址形式如 xxx@qq.com ,xxx@163.com .
总结成正则表达式如下:
"^([a-z0-9A-z]+[-|\\.]?)+[a-z0-9A-z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+([a-zA-Z]{2,})$"
用Java编写一个检查有效性的例子:
public static boolean matchTest(String email){boolean a = email.matches("^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+([a-zA-Z]{2,})$");System.out.println(a);return a;}
编写完整的拦截器:
import org.example.service.Testjson;
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;
@Component
public class EmailInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String email = request.getParameter("email");if (Testjson.matchTest(email)){return true;}else {return false;}}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}
这里只需要重写 preHandle 方法即可,完成对有效性邮箱地址的判断效果。
3.Token 检查拦截器实现
Token检查是一个健全系统或者服务必须有的一项安全性功能,特别是基于 RESTful 的接口,需要检查Token来判断用户是否已经登陆或者验证身份,利用Token可以获取进一步的网络资源和权限。
一般我们会使用 JWT 验证方式,JSON Web Token 简称 JWT。本质上是一段字符串,由以下三个部分构成。
- 头部: JWT 基本信息,包括算法。
- 荷载: 自定义数据,包括用户标识,如 userid。
- 签名: 前面两者通过一定加密算法后计算所得其签名字符串。
引入 JWT 依赖到 pom.xml 文件中,代码如下:
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.4.0</version></dependency>
更新前端拦截器配置,处理所有请求:
@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate LoggerInterceptor loggerInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loggerInterceptor).addPathPatterns("/**").excludePathPatterns("/ls");//不拦截的路径registry.addInterceptor(authenticationInterceptor()).addPathPatterns("/**");}@Beanpublic AuthenticationInterceptor authenticationInterceptor(){return new AuthenticationInterceptor();}
}
新建两个注解,用于判断是否进行Token验证,首先编写需要登陆验证的注解UserLoginToken:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 需要登陆才能操作得注解*/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {boolean required() default true;
}
用于跳过验证的注解:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {boolean required() default true;
}
检查Token的拦截器:
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import org.example.Exception.BusinessException;
import org.example.pojo.User;
import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
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;@Component
public class AuthenticationInterceptor implements HandlerInterceptor {@AutowiredUserService userService;@AutowiredRedisUtil redisUtil;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String token = request.getHeader("token");//排除访问的是静态资源,而不是映射访问if (!(handler instanceof HandlerMethod)){return true;}//获取访问的方法HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();//是否被跳过验证注解if (method.isAnnotationPresent(PassToken.class)){PassToken passToken = method.getAnnotation(PassToken.class);if (passToken.required()){return true;}}if (method.isAnnotationPresent(UserLoginToken.class)){UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);if (userLoginToken.required()){if (token == null){throw new BusinessException("4001","no token");}String userId;try{userId = JWT.decode(token).getAudience().get(0);}catch (Exception e){throw new BusinessException("4003","decode token fails");}//Check the expire of tokenString tokenKey = userId+":"+token;boolean hasExisted = redisUtil.hasKey(tokenKey);System.out.println("exit or not: "+hasExisted);if (!hasExisted){throw new BusinessException("4005","token expired!");}int userID = Integer.parseInt(userId);System.out.println("userId is:"+userID);User user =userService.findUserById(userID);if (user == null){throw new RuntimeException("user not exits");}try {JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword()+"MTest!76&sQ^")).build();jwtVerifier.verify(token);//设置当前登录用户LoginUser loginUser = new LoginUser();loginUser.setID((long)userID);UserContext.setUser(loginUser);}catch (JWTVerificationException e){throw new BusinessException("4002","invalid token");}}}return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}
使用起来也很方便,在需要与不需要进行 Token 验证的接口前面加上对应的注释即可。
补充:
handler参数是 Spring MVC 框架传递给你的preHandle方法的一个对象,它代表当前请求即将要调用的控制器逻辑。在 Spring MVC 中,这个handler对象可以是多种类型,但通常它是HandlerMethod的一个实例。当一个请求被映射到某个控制器的方法时,这个方法就被包装为
HandlerMethod对象。HandlerMethod对象包含了这个方法以及包含这个方法的控制器的信息,允许你在拦截器中对即将执行的控制器方法进行一些前处理。在某些情况下,
handler可能不是HandlerMethod的实例。例如,当请求是针对静态资源时,或者是某个请求没有被映射到任何的控制器上的方法时,handler可能是框架中其他类型的处理器,这取决于你的应用配置和请求的具体类型。这就是为什么在preHandle方法中进行instanceof HandlerMethod检查是一种常见的做法,以确保你正在处理的是一个具体的控制器方法。如果是,则可以安全地转型到HandlerMethod并进行进一步的处理。在
preHandle方法的上下文中,你可以对handler参数进行各种检查和操作。例如,你可以检查用户是否有权限执行与handler对应的控制器方法,你可以修改请求或响应,或者决定是否继续执行请求处理链(通过返回true或者false)。
在Spring MVC框架中:
静态资源请求:
这些请求通常不需要经过复杂的处理,它们被映射到服务器上存储静态文件的目录。例如在Spring Boot中,静态资源可以放置在 `/static`、`/public` 等默认的资源目录中,这些目录中的资源可以直接通过URL被访问。在这种情况下,与静态资源对应的`handler`可能就不是一个`HandlerMethod`,而是其他用于处理静态资源的类的实例。数据请求:
这些请求需要后端处理,如执行数据库操作、进行计算或调用其他接口等。在Spring MVC中,这些请求被映射到控制器类中的特定方法上,这些方法通过注解(如`@RequestMapping`或其特化版本比如`@GetMapping`、`@PostMapping`等)来标注。这时候传递给`preHandle`方法的`handler`参数就是一个`HandlerMethod`实例,代表被映射的那个方法。因此,在拦截器的`preHandle`方法中,进行`handler instanceof HandlerMethod`检查可以帮助你明确该次请求是需要Spring MVC通过控制器中的方法处理,还是仅仅是为了获取静态资源。这可以确保你的拦截器逻辑只应用于实际需要拦截的后端处理请求,而不是静态资源请求。
相关文章:
拦截器的使用
拦截器(Interceptor)是一种在应用程序中用于干预、修改或拦截请求和响应的组件,是AOP 编程的一种实践,和过滤器一样都是一种具体的AOP实现。它可以在请求被发送到目标处理程序之前或之后,对请求进行预处理或对响应进行…...
R语言——taxize(第四部分)
taxize(第四部分) 3.39. get_wiki(获取维基分类群的页面名称)3.40. get_wormsid(获取分类群名称的Worms ID)3.41. gni_details(使用Global Names Index搜索分类学名称详情)3.42. gni…...
C++学习 --list
目录 1, 什么是list 2, 创建 2-1, 标准数据类型 2-2, 自定义数据类型 2-3, 其他创建方式 3, 操作list 3-1, 赋值 3-2, 添加元素 3-2-1, 添加元素(assign) 3-2-…...
Springboot集成swagger之knife4j
knife4j的最终效果: 支持直观的入参介绍、在线调试及离线各种API文档下载。 1 引入pom <dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>3.0.2</ver…...
多线程 02
1.线程的常见构造方法 方法说明Thread()创建线程对象Thread(Runnable target)使用 Runnable 对象创建线程对象Thread(String name)创建线程对象,并命名Thread(Runnable target, String name)使用 Runnable 对象创建线程对象,并命名【了解】Thread(Threa…...
车辆管控大数据可视化平台案例源码分析【可视化项目案例-10】
🎉🎊🎉 你的技术旅程将在这里启航! 🚀🚀 本专栏包括但不限于大屏可视化、图表可视化等等。订阅专栏用户在文章底部可下载对应案例源码以供大家深入的学习研究。 🎓 每一个案例都会提供完整代码和详细的讲解,不论你是初学者还是资深开发者,这里都有适合你的内容。…...
链表的回文结构
题目描述 题目链接:链表的回文结构_牛客题霸_牛客网 (nowcoder.com) 题目分析 我们的思路是: 找到中间结点逆置后半段比对 我们可以简单画个图来表示一下: ‘ 奇数和偶数都是可以的 找中间结点 我们可以用快慢指针来找中:l…...
CSS特效017:球体涨水的效果
CSS常用示例100专栏目录 本专栏记录的是经常使用的CSS示例与技巧,主要包含CSS布局,CSS特效,CSS花边信息三部分内容。其中CSS布局主要是列出一些常用的CSS布局信息点,CSS特效主要是一些动画示例,CSS花边是描述了一些CSS…...
Windows下安装Anaconda3并使用JupyterNoteBook
下载安装包 Anaconda官网 进官网,点击下载 自动根据当前系统下载对应的包了,安装包大约1G,喝杯Java耐心等待。 安装 很多人安装C盘,我这里放D盘。 注意:你的文件夹目录一定要不能有空格 然后其他的直接默认install即…...
什么年代了,还不会 CI/CD 么?
目录 什么是 CI/CD? CI/CD 对业务有哪些好处? 一:确保卓越的代码质量 二:更快的发布速度 → 更快的交付 三:自动化降低成本 四:故障隔离 五:简化回滚 六:持续反馈 七&#…...
centos 7.7 安装Python-3.7.4
一、安装PYTHON 编译依赖包 1.1 首先安装gcc编译器,gcc有些系统版本已经默认安装,通过 gcc --version 查看,没安装的先安装gcc, yum -y install gcc glibc make1.2 安装其它依赖包,(注:不要缺…...
git的用法
目录 一、为什么需要git 二、git基本操作 2.1、初始化git仓库 2.2、配置本地仓库的name和email 2.3、认识工作区、暂存区、版本库 三、git的实际操作 3.1 提交文件 3.2 查看git状态以及具体的修改 3.3 git版本回退 git reset 3.1 撤销修改 四、git分支管理 4.…...
管道在Vue和Angular中的作用及React的替代方案
管道在Vue和Angular中的作用及React的替代方案 前言管道起源管道特点 前端中管道概念和作用概念作用 React关于管道的替代方案Vue和Angular管道的区别 前言 本文主要讲解管道在Vue和Angular中有哪些作用以及React对于管道概念的替代方案是什么。 管道起源 计算机中的Pipline…...
计算机基础知识57
前后端数据传输的编码格式(contentType) # 我们只研究post请求方式的编码格式: get请求方式没有编码格式-- index?useranme&password get请求方式没有请求体,参数直接在url地址的后面拼接着 # 有哪些方式可以提交post请求:f…...
Flutter 小技巧之 3.16 升级最坑 M3 默认适配技巧
如果要说 Flutter 3.16 升级里是最坑的是什么?那我肯定要说是 Material 3 default (M3)。 倒不是说 M3 bug 多,也不是 M3 在 3.16 上使用起来多麻烦,因为虽然从 3.16 开始,MaterialApp 里的 useMaterial3 …...
激光雷达与惯导标定 | Lidar_IMU_Init : 编译
激光雷达与惯导标定:Lidar_IMU_Init 编译 功能包安装安装ceres-solver-2.0.0 (注意安装2.2.0不行,必须要安装2.0.0) LI-Init是一种鲁棒、实时的激光雷达惯性系统初始化方法。该方法可校准激光雷达与IMU之间的时间偏移量和外部参数…...
进程池,线程池与跨进程数据共享爬取某岸网图片
看教程的时候看到一个,生产者跟消费者的概念比较有意思,但是给的代码有问题无法正常运行,于是我就捣鼓了一下。 基本概念就是: 生产者: 一个进程获取网页没页的图片连接(主进程…...
【 图片加载】Vue前端各种图片引用
文章目录 一、图片作为js常量(常作为配置项的值 )1、在线链接2、本地图片 二、图片img标签1、一般的src2、动态的src用require3、src可以接收二进制文件blob(如后端返回的、a-upload传的图片) 三、背景图片 一、图片作为js常量(常…...
thinkphp6生成PDF自动换行
composer安装 composer require tecnickcom/tcpdf 示例 use TCPDF;public function info($university,$performance,$grade,$major){//获取到当前域名$domain request()->domain();//实例化$pdf new TCPDF(P, mm, A4, true, UTF-8, false);// 设置文档信息$pdf->SetCr…...
wpf devexpress实现输入验证使用验证规则
打开此项目 目标是一个registration form行为像google registration form。打开Google registration form 研究它的行为。当form是第一次显示,它的“Register”按钮应该启动;编辑器没有提示任何输入错误。输入First Name编辑器字段,清理输入…...
Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...
HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...
python爬虫——气象数据爬取
一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用: 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests:发送 …...
实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...
React从基础入门到高级实战:React 实战项目 - 项目五:微前端与模块化架构
React 实战项目:微前端与模块化架构 欢迎来到 React 开发教程专栏 的第 30 篇!在前 29 篇文章中,我们从 React 的基础概念逐步深入到高级技巧,涵盖了组件设计、状态管理、路由配置、性能优化和企业级应用等核心内容。这一次&…...
CSS3相关知识点
CSS3相关知识点 CSS3私有前缀私有前缀私有前缀存在的意义常见浏览器的私有前缀 CSS3基本语法CSS3 新增长度单位CSS3 新增颜色设置方式CSS3 新增选择器CSS3 新增盒模型相关属性box-sizing 怪异盒模型resize调整盒子大小box-shadow 盒子阴影opacity 不透明度 CSS3 新增背景属性ba…...
