Java拦截器(Interceptor)和过滤器(Filter)实例详解
一、Java过滤器和拦截器
1.1、过滤器(Filter)
Filter过滤器,是Servlet(Server Applet)技术中的技术,开发人员可以通过Filter技术,管理web资源,可以对指定的一些行为进行拦截,例如URL级别的权限访问控制等。在ServletRequest到达Servlet之前,拦截客户端的ServletRequest,可以根据需要检查ServletRequest,也可以修改ServletRequest中的头和数据;在ServletResponse到达客户端之前,可以拦截ServletResponse,可以根据需要检查ServletResponse,同样也可以修改。
通过实现Filter接口(属于javax.servlet包),实现doFilter()方法,即可自定义过滤器。
1.2、拦截器(Interceptor)
Interceptor拦截器是Spring MVC框架中对请求进行拦截和处理的组件,可以实现权限验证、日志记录、异常处理等功能,拦截器是在Spring MVC框架中执行的。在HttpServletRequest到达Controller之前,可以根据需要检查HttpServletRequest,也可以进行修改;在HttpServletResponse返回之前,可以根据需要检查HttpServletResponse,也可以进行修改。
通过实现HandlerInterceptor接口(属于org.springframework.web.servlet包),实现preHandle(前置)、postHandle(后置)、afterCompletion(完成后)方法,即可自定义拦截器。
1.3、两者区别
相同点:
1、拦截器和过滤器都体现了AOP的思想,都可以拦截请求方法;
2、过滤器和拦截器可以定义多个,且可以通过Order自定义执行顺序;
不同点:
1、运行位置不同:过滤器运行在Web服务器和Servlet容器之间,拦截器是针对具体控制器方法前进行拦截;
2、功能不同:过滤器主要对请求进行预处理或者过滤;而拦截器主要对请求进行流程控制;
3、依赖框架不同:过滤器是基于Servlet实现的,而拦截器是基于Spring MVC的。
二、代码应用
2.1、需求说明
通过过滤器实现初步过滤,筛除掉一些非法IP访问,例如不允许本机(127.0.0.1)外的所有IP地址访问。通过拦截器实现登录信息或身份信息校验,并实现日志记录功能。
2.2、过滤器编写
import com.hao.dockertest.util.MyTimeUtil;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;/*** @author Hao* @program: DockerTest* @description: 过滤器* @date 2023-10-21 21:11:28*/
@Slf4j
public class MyFilter implements Filter {@Overridepublic void init(javax.servlet.FilterConfig filterConfig) throws ServletException {log.info("Filter init!");Filter.super.init(filterConfig);}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {log.info("Filter Start! 实现初步过滤");// 从ServletRequest获取相关信息String ip = servletRequest.getRemoteAddr();if (ip.equals("0:0:0:0:0:0:0:1")) ip = "127.0.0.1";String time = MyTimeUtil.sdf.format(System.currentTimeMillis());log.info("访问IP:{},访问时间:{}", ip, time);// 拦截非本机IPif(!ip.equals("127.0.0.1")){log.error("非本机IP({})禁止访问!", ip);// 自定义ServletResponse,返回前端提示信息PrintWriter printWriter = servletResponse.getWriter();servletResponse.setCharacterEncoding("UTF-8");servletResponse.setContentType("application/json;charset=UTF-8");printWriter.write("Sorry, your IP address does not allow access!");return;}// 手动放行(如果上面加了过滤条件,最后必须手动放行!)filterChain.doFilter(servletRequest, servletResponse);}@Overridepublic void destroy() {log.info("Filter destroy!");javax.servlet.Filter.super.destroy();}
}
注册自定义的过滤器
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.servlet.Filter;/*** @author Hao* @program: DockerTest* @description: 定义过滤器* @date 2023-10-21 21:18:05*/
@Configuration
public class FilterConfig {@Beanpublic FilterRegistrationBean<Filter> filterRegistrationBean(){FilterRegistrationBean<Filter> filterFilterRegistrationBean = new FilterRegistrationBean<>();// 注册自定义的过滤器filterFilterRegistrationBean.setFilter(new MyFilter());return filterFilterRegistrationBean;}
}
实现效果:
可以看到非本地IP的访问已经被拦截了。
本地IP的访问可以被正常放行。
2.3、拦截器编写
import com.hao.dockertest.util.JWTUtil;
import com.hao.dockertest.util.MyTimeUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;/*** @author Hao* @program: DockerTest* @description: 拦截器* @date 2023-10-21 21:13:27*/
@Slf4j
@Configuration
public class MyInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info("拦截器前置处理 preHandle");// 验证tokenString token = request.getHeader("token");if(token == null || !JWTUtil.checkToken(token)){log.error("未登录或身份信息验证失败");response.setCharacterEncoding("UTF-8");response.setContentType("application/json;charset=UTF-8");PrintWriter printWriter = response.getWriter();printWriter.write("您还未登录或身份验证失败,请重新登录!");return false;}return HandlerInterceptor.super.preHandle(request, response, handler);}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("拦截器后置处理 postHandle");// 从HttpServletRequest获取相关信息String ip = request.getRemoteAddr();if (ip.equals("0:0:0:0:0:0:0:1")) ip = "127.0.0.1";String browser = request.getHeader("Sec-Ch-Ua-Platform");String httpMethod = request.getMethod();String time = MyTimeUtil.sdf.format(System.currentTimeMillis());// 日志存档// Record record = new Record(null, ip, httpMethod, browser, time);// 访问日志记录逻辑// 从token中获取相关信息String token = request.getHeader("token");String userName = JWTUtil.getTokenInfo(token, "userName");String userId = JWTUtil.getTokenInfo(token, "userId");log.info("访问用户:{}-{},访问IP:{},访问时间:{},请求方式:{},访问设备:{}",userName, userId, ip, time, httpMethod, browser);HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {log.info("拦截器完成后 afterCompletion");HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}
}
配置自定义的拦截器
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;/*** @author Hao* @program: DockerTest* @description: Interceptor配置* @date 2023-10-21 21:26:18*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {@Autowiredprivate MyInterceptor myInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(myInterceptor).addPathPatterns("/**").excludePathPatterns("/login"); // 拦截所有请求,排除login}
}
2.4、JWT工具
首先引入JWT工具jjwt的pom依赖
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency>
自定义JWT工具类,包含构建Token,检验token有效性和解析token。
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;import java.util.Date;
import java.util.UUID;/*** @author Hao* @program: DockerTest* @description: JWT工具* @date 2023-10-23 10:44:36*/
@Slf4j
public class JWTUtil {private static long time = 1000*60*60*10;// 签名private static final String signature = "test";public static String createToken(String userName, String userID){JwtBuilder jwtBuilder = Jwts.builder();//构建JWT对象return jwtBuilder// Header.setHeaderParam("typ","JWT").setHeaderParam("alg","HS256")// payload.claim("userName",userName).claim("userId", userID)// 设置有效期(毫秒单位).setExpiration(new Date(System.currentTimeMillis()+time)).setId(UUID.randomUUID().toString())// signature.signWith(SignatureAlgorithm.HS256, signature)// compact拼接三部分header、payload、signature.compact();}public static Boolean checkToken(String token){if(token == null){return false;}try {JwtParser jwtParser = Jwts.parser();jwtParser.setSigningKey(signature).parseClaimsJws(token);return true;}catch (Exception e){// log.error("token失效");return false;}}public static String getTokenInfo(String token, String key){if(token == null || key == null) return null;JwtParser parser = Jwts.parser();Jws<Claims> claimsJws = parser.setSigningKey(signature).parseClaimsJws(token);Claims payload = claimsJws.getBody();// 获取key对于的内容return payload.get(key).toString();}}
2.5、模拟Controller
主要模拟访问业务和登录业务
import com.hao.dockertest.util.JWTUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;/*** @author Hao* @program: DockerTest* @description: 模拟Controller* @date 2023-10-20 12:13:28*/
@RestController
@RequestMapping
@Slf4j
public class RecordController {@GetMappingpublic String getInfo(){return "Welcome!";}@PostMapping("/login")public String login(){return JWTUtil.createToken("张三", "20210919"); // 模拟返回登录成功token}}
其中getinfo()为模拟某个业务,需要登陆之后才能访问;而login()是登录方法,返回给前端token,这个方法拦截器默认放行。
2.6、测试
我们使用PostMan模拟一下拦截器功能
可以看到,过滤器先启动,初步过滤掉非法IP,然后拦截器启动,验证token信息,在postman的Headers中我们并未设置token,拦截器检查到HttpServletRequest不包含token,会返回前端错误信息,提示用户登录。
然后我们访问login:
可以看到这里只有过滤器起了作用,因为我们拦截器设置了排除/login这个路径,因此/login不会被拦截器拦截,也就可以正常的访问controller中的login方法,在postman中我们可以看到已经正常访问,并返回了token。
将返回的token,我们手动加入到Headers的token中,再访问一次上面模拟的业务逻辑:
可以看到已经可以正常访问,再看一下控制台日志:
可以看到拦截器正常拦截,并从中获取到了关键信息,打印了日志,并成功放行,基于此就实现了基于拦截器的身份验证功能。
此外,我们还可以模拟一下token失效或者被非法篡改,看一下拦截效果。我们随机把Headers的token删除几个字符,再试一下访问效果。
可以看到,即使携带token,但token已经实现,就无法对controller中的方法进行访问,被拦截器成功拦截。
三、应用场景
常见的过滤器用途主要包括:对用户请求进行统一认证、对用户访问的请求进行记录和审核,对用户发送的数据进行过滤或替换,转换图像格式、对响应内容进行压缩,对请求或响应进行加密解密等。
拦截器采用了AOP的设计思想,可以用来拦截处理方法在之前和之后执行的一些跟主业务没有关系的公共功能,例如权限控制,日志、异常记录,记录方法执行时间等。
相关文章:

Java拦截器(Interceptor)和过滤器(Filter)实例详解
一、Java过滤器和拦截器 1.1、过滤器(Filter) Filter过滤器,是Servlet(Server Applet)技术中的技术,开发人员可以通过Filter技术,管理web资源,可以对指定的一些行为进行拦截,例如URL级别的权限…...

通过热敏电阻计算温度(二)---ODrive实现分析
文章目录 通过热敏电阻计算温度(二)---ODrive实现分析测量原理图计算分析计算拟合的多项式系数根据多项式方程计算温度的函数温度计算调用函数 通过热敏电阻计算温度(二)—ODrive实现分析 ODrive计算热敏电阻的温度采用的时B值的…...
基于typescript+express实现一个简单的接口权限验证
package.json "scripts": {"start": "nodemon src/main.ts","start:a": "nodemon src/a.ts","build": "tsc","build:dev": "tsc src/main.ts"}, express服务器文件 import * as…...

yolov7改进优化之蒸馏(二)
续yolov7改进优化之蒸馏(一)-CSDN博客 上一篇已经基本写出来yolov7/v5蒸馏的整个过程,不过要真的训起来我们还需要进行一些修改。 Model修改 蒸馏需要对teacher和student网络的特征层进行loss计算,因此我们forward时要能够返回需…...
生产与作业管理(POM)的历史
1800年,惠特尼:零件标准化、质量管理。 1881年,泰勒:人员选拔、计划和时程安排、动作研究。管理与劳动分开。 - 使雇员与工作相适应。 - 提供适当的训练。 - 提供正确的工作方法和工具。 - 建立适当的激励机制促使工作得以完成。 …...
交换机基础(二)
一、VLAN 基础知识 虚拟局域网 (Virtual Local Area Network,VLAN) 是一种将局域网设 备从逻辑上划分成一个个网段,从而实现虚拟工作组的数据交换技术。 这一技术主要应用于3层交换机和路由器中,但主流应用还是在3层交换机中。 VLAN 是基于物理网络上构建…...

回归预测 | MATLAB实现基于BP-Adaboost的BP神经网络结合AdaBoost多输入单输出回归预测
回归预测 | MATLAB实现基于BP-Adaboost的BP神经网络结合AdaBoost多输入单输出回归预测 目录 回归预测 | MATLAB实现基于BP-Adaboost的BP神经网络结合AdaBoost多输入单输出回归预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.MATLAB实现基于BP-Adaboost的BP…...

【蓝桥每日一题]-动态规划 (保姆级教程 篇11)#方格取数2.0 #传纸条
目录 题目:方格取数 思路: 题目:传纸条 思路: 题目:方格取数 (跑两次) 思路: 如果记录一种方案后再去跑另一个方案,影响因素太多了,所以两个方案要同时开…...

前端TypeScript学习day05-索引签名、映射与类型声明文件
(创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹) 目录 索引签名类型 映射类型 索引查询(访问)类型 基本使用 同时查询多个索引的类型…...

Echarts柱状图数据过多设置滚动条效果
未设置前: 设置后: dataZoom: [ { show: true, height:8, bottom:0, startValue: 0, //起始值 endValue: 5, //结束值 showDetail: fals…...

64 最长公共子序列
最长公共子序列 题解1 DP 给定两个字符串 text1 和 text2,返回这两个字符串的 最长公共子序列的长度。如果不存在 公共子序列,返回 0 。 一个字符串的子序列是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些…...
matlab常用函数
绘图函数 一、plot():二维图形绘制 1、plot(y): 对于只含一个输入参数的plot函数,如果输入参数y为向量,则以该参数为纵坐标,横坐标从1开始至与向量的长度相等;如果输入参数y是矩阵时,则按列绘…...
Python配置镜像源
Python3安装pika的准备 Windows下配置镜像源可以按照如下操作。 1.winR执行%APPDATA% %APPDATA%后,创建pip文件夹,并创建pip.ini配置文件 查看此目录下是否有pip目录,如果没有则需要创建,并在pip目录下以文本方式添加pip.ini文件…...
Linux防火墙Centos6的常用命令iptables
文章目录 一、iptables基础知识二、作者玩玩的配置文件三、iptables中常用的参数以及作用-j参数的动作类型 四、安装iptables五、iptables启动命令六、iptables命令结构命令例子默认执行方式执行iptables命令和写入配置文件两种方式的对比 相对常用的命令参考文档 一、iptables…...
python中的贪心算法-求顾客的最小的等待时间
一. 设有n个顾客同时等待一项服务。顾客i需要的服务时间为ti(1<i<n)。如何安排n个顾客的服务次序才能使顾客总的等待时间达到最小? nint(input(请输入顾客的位数: ))times[] for i in range(n):timeint(input(f请输入顾客{i1}的服务时间: ))times.append(time) times.so…...
【JAVA springframework.http】如何发送HTTP请求
Springboot之restTemplate https://blog.csdn.net/weixin_43702146/article/details/116567707 public Result doHandlePostJson(String restUri, String jsonData)throws Exception {Result result null;try {// logger记录log.info("doHandlePostJson request restUr…...
字符串反转(Python)
1. 整体流程 为了实现递归反转n个字符串的功能,我们可以按照以下步骤进行操作: 步骤动作1定义递归函数2判断递归结束条件3处理递归函数的基本情况4调用递归函数,递归处理子问题5返回递归结果 我将详细解释每一步的具体操作,并提…...

驱动开发day4
通过字符设备驱动的分步实现编写LED驱动,另外实现设备文件和驱动的绑定 head.h #ifndef __HEAD_H__ #define __HEAD_H__ typedef struct {unsigned int MODER;unsigned int OTYPER;unsigned int OSPEEDR;unsigned int PUPDR;unsigned int IDR;unsigned int ODR; }…...

Flink之Window窗口机制
窗口Window机制 窗口概述窗口的分类是否按键分区按键分区窗口非按键分区 按照驱动类型按具体分配规则滚动窗口Tumbling Windows滑动窗口 Sliding Windows会话窗口 Session Windows全局窗口 Global Windows 时间语义窗口分配器 Window Assigners时间窗口计数窗口例子 窗口函数 W…...

【C++】继承 ⑧ ( 继承 + 组合 模式的类对象 构造函数 和 析构函数 调用规则 )
文章目录 一、继承 组合 模式的类对象 构造函数和析构函数调用规则1、场景说明2、调用规则 二、完整代码示例分析1、代码分析2、代码示例 一、继承 组合 模式的类对象 构造函数和析构函数调用规则 1、场景说明 如果一个类 既 继承了 基类 ,又 在类中 维护了一个 其它类型 的…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...

STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...