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、场景说明 如果一个类 既 继承了 基类 ,又 在类中 维护了一个 其它类型 的…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
