Spring Boot过滤器链:从入门到精通
文章目录
- 一、过滤器链是什么?
- 二、为什么需要过滤器链?
- 三、Spring Boot中的过滤器链是如何工作的?
- (一)过滤器的生命周期
- (二)过滤器链的执行流程
- 四、如何在Spring Boot中定义自己的过滤器?
- 五、如何控制过滤器的顺序?
- 六、Spring Security中的过滤器链
- 七、过滤器链的性能优化
- 八、总结
在当今的Web开发中,Spring Boot凭借其简洁高效的特性,成为了众多开发者的首选框架。而过滤器链作为Spring Boot中一个重要的概念,对于保障应用的安全性和功能完整性起着关键作用。今天,就让我们一起深入了解Spring Boot中的过滤器链,从入门到精通,一步步揭开它的神秘面纱。
一、过滤器链是什么?
在Spring Boot中,过滤器链(Filter Chain)是由一系列过滤器(Filter)组成的有序集合。这些过滤器按照一定的顺序依次对请求进行处理,每个过滤器都可以对请求进行拦截、修改或增强操作,从而实现诸如安全检查、日志记录、请求转发等功能。过滤器链的设计使得我们可以将不同的功能模块化,通过组合多个过滤器来实现复杂的功能逻辑,同时保持代码的清晰和可维护性。
二、为什么需要过滤器链?
在Web应用中,我们常常需要在请求到达业务逻辑之前或之后执行一些通用的操作。例如,在请求到达控制器之前,我们可能需要验证用户的身份、记录请求的日志;在响应返回给客户端之前,我们可能需要对响应内容进行压缩或加密。如果这些操作都直接写在业务逻辑代码中,会导致代码的耦合度很高,难以维护和扩展。而过滤器链的出现,正是为了解决这个问题。通过将这些通用操作封装到不同的过滤器中,并按照一定的顺序组织成过滤器链,我们可以在不修改业务逻辑代码的情况下,灵活地添加或修改这些通用操作,从而提高代码的可维护性和可扩展性。
三、Spring Boot中的过滤器链是如何工作的?
(一)过滤器的生命周期
在Spring Boot中,每个过滤器都遵循一个标准的生命周期,主要包括以下三个阶段:
- 初始化(init):当过滤器被创建时,Spring会调用过滤器的init方法。在这个方法中,我们可以进行一些初始化操作,例如加载配置文件、初始化资源等。不过,在Spring Boot中,由于过滤器通常是通过Spring的依赖注入机制进行管理的,因此我们很少需要手动实现init方法。
- 过滤(doFilter):这是过滤器的核心方法,当请求到达过滤器时,Spring会调用doFilter方法。在这个方法中,我们可以对请求进行拦截、修改或增强操作。例如,我们可以在doFilter方法中验证用户的身份,如果用户未登录,则直接返回401状态码;如果用户已登录,则继续调用下一个过滤器。doFilter方法的参数包括ServletRequest、ServletResponse和FilterChain,其中FilterChain表示过滤器链的下一个过滤器,通过调用FilterChain的Filterdo方法,可以将请求传递给下一个过滤器。
- 销毁(destroy):当过滤器被销毁时,Spring会调用过滤器的destroy方法。在这个方法中,我们可以进行一些清理操作,例如关闭资源、释放内存等。和init方法一样,在Spring Boot中,我们也很少需要手动实现destroy方法。
(二)过滤器链的执行流程
当一个请求到达Spring Boot应用时,Spring会按照过滤器链中过滤器的顺序依次调用每个过滤器的doFilter方法。每个过滤器都可以对请求进行处理,然后通过调用FilterChain的doFilter方法将请求传递给下一个过滤器。如果某个过滤器不想让请求继续传递下去,它可以选择不调用FilterChain的doFilter方法,从而直接返回响应给客户端。例如,如果一个过滤器发现用户未登录,它可以直接返回401状态码,而不再调用下一个过滤器。
过滤器链的执行流程可以用以下伪代码来表示:
filterChain.doFilter(ServletRequest request, ServletResponse response) {for (Filter filter : filters) {filter.doFilter(request, response, new FilterChain() {@Overridepublic void doFilter(ServletRequest request, ServletResponse response) {// 调用下一个过滤器FilterChain.doFilter(request, response);}});}
}
从这个伪代码中可以看出,过滤器链的执行是一个递归的过程,每个过滤器都负责调用下一个过滤器的doFilter方法,直到所有的过滤器都执行完毕,请求才会最终到达业务逻辑代码。
四、如何在Spring Boot中定义自己的过滤器?
在Spring Boot中,定义自己的过滤器非常简单。我们只需要创建一个类,让它实现javax.servlet.Filter接口,然后在类上添加@Component注解,让Spring能够自动扫描并注册这个过滤器。以下是一个简单的自定义过滤器示例:
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;@Component
public class MyFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {// 初始化操作System.out.println("MyFilter初始化");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {// 在请求到达业务逻辑之前执行的操作System.out.println("MyFilter请求前处理");HttpServletRequest httpRequest = (HttpServletRequest) request;System.out.println("请求路径:" + httpRequest.getRequestURI());// 调用下一个过滤器chain.doFilter(request, response);// 在响应返回给客户端之前执行的操作System.out.println("MyFilter响应后处理");}@Overridepublic void destroy() {// 销毁操作System.out.println("MyFilter销毁");}
}
在上面的代码中,我们定义了一个名为MyFilter的过滤器。在doFilter方法中,我们在请求到达业务逻辑之前打印了一条日志,并获取了请求的路径;在响应返回给客户端之前,我们又打印了一条日志。通过在类上添加@Component注解,Spring会自动扫描并注册这个过滤器,将其加入到过滤器链中。
五、如何控制过滤器的顺序?
在Spring Boot中,过滤器链中过滤器的执行顺序是由过滤器的优先级决定的。默认情况下,Spring会按照过滤器的类名的字典顺序对过滤器进行排序。但是,我们可以通过实现Ordered接口或使用@Order注解来指定过滤器的优先级。优先级越小,过滤器越先执行。
以下是一个使用@Order注解指定过滤器优先级的示例:
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;import javax.servlet.*;
import.io java.IOException;@Component
@Order(1)
public class FirstFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("FirstFilter初始化");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println("FirstFilter请求前处理");chain.doFilter(request, response);System.out.println("First响应Filter后处理");}@Overridepublic void destroy() {System.out.println("FirstFilter销毁");}
}@Component
@Order(2)
public class SecondFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("SecondFilter初始化");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println("SecondFilter请求前处理");chain.doFilter(request, response);System.out.println("SecondFilter响应后处理");}@Overridepublic void destroy() {System.out.println("SecondFilter销毁");}
}
在上面的代码中,我们定义了两个过滤器FirstFilter和SecondFilter,并通过@Order注解分别指定了它们的优先级为1和2。因此,在过滤链器中,FirstFilter会先于SecondFilter执行。
六、Spring Security中的过滤器链
Spring Security是Spring Boot中用于实现安全认证和授权的框架,它也使用了过滤器链来实现各种安全功能。Spring Security的过滤器链中包含了许多预定义的过滤器,例如SecurityContextPersistenceFilter用于在请求开始时恢复安全上下文,在请求结束时清理安全上下文;UsernamePasswordAuthenticationFilter用于处理基于用户名和密码的登录请求;ExceptionTranslationFilter用于处理安全相关的异常,例如用户未登录或没有权限访问某个资源等。
通过自定义过滤器并将其加入到Spring Security的过滤器链中,我们可以扩展Spring Security的功能,实现自己的安全需求。例如,我们可以通过自定义一个过滤器来实现基于JWT(JSON Web Token)的认证机制,或者通过自定义一个过滤器来实现对请求的访问控制。
以下是一个将自定义过滤器加入到Spring Security过滤器链中的示例:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.addFilterBefore(new MyCustomFilter(), UsernamePasswordAuthenticationFilter.class).authorizeRequests().anyRequest().authenticated().and().formLogin().permitAll().and().logout().permitAll();}
}
在的上面代码中,我们通过addFilterBefore方法将自定义的MyCustomFilter加入到了Spring Security的过滤器链中,并指定它在UsernamePasswordAuthenticationFilter之前执行。这样,我们就可以在用户登录之前对请求进行处理,例如验证请求的来源是否合法等。
七、过滤器链的性能优化
虽然过滤器链为我们提供了强大的功能,但是过多的过滤器或不合理的过滤器链设计可能会对应用的性能产生影响。以下是一些优化过滤器链性能的建议:
减少不必要的过滤器:只在真正需要的地方使用过滤器,避免在每个请求上都执行不必要的操作。例如,如果某个过滤器只对特定的请求路径有效,可以通过在doFilter方法中添加路径匹配逻辑来减少不必要的调用。
优化过滤器的实现:在过滤器的doFilter方法中,尽量减少对资源的占用和对性能的影响。例如,避免在过滤器中进行复杂的计算或大量的I/O操作。
合理安排过滤器的顺序:将那些可以快速拒绝请求的过滤器放在前面,例如安全过滤器或权限过滤器,这样可以减少不必要的后续处理。
使用异步过滤器:如果过滤器的操作可以异步执行,可以考虑使用AsyncFilter来提高性能。异步过滤器可以在不阻塞主线程的情况下执行耗时操作,从而提高应用的并发能力。
八、总结
通过本文的介绍,我们从过滤器链的基本概念出发,逐步深入到了Spring Boot中过滤器链的实现原理、定义方法、顺序控制以及与Spring Security的结合等方面。过滤器链作为一种强大的功能,可以帮助我们实现各种通用的操作,提高代码的可维护性和可扩展性。然而,在使用过滤器链时,我们也需要注意性能优化,避免对应用的性能产生负面影响。
相关文章:
Spring Boot过滤器链:从入门到精通
文章目录 一、过滤器链是什么?二、为什么需要过滤器链?三、Spring Boot中的过滤器链是如何工作的?(一)过滤器的生命周期(二)过滤器链的执行流程 四、如何在Spring Boot中定义自己的过滤器&#…...

vue3之echarts3D圆柱
vue3之echarts3D圆柱 效果: 版本 "echarts": "^5.1.2" 核心代码: <template><div ref"charts" class"charts"></div><svg><linearGradient id"labColor" x1"0&q…...
Redux中间件redux-thunk和redux-saga的具体区别是什么?
Redux 中间件是增强 Redux 功能的重要工具,redux-thunk 和 redux-saga 是两个常用的中间件,它们在处理异步操作和副作用时提供了不同的方式和理念。以下是两者的具体区别: 1. 概念与设计理念 redux-thunk 简洁:redux-thunk 是一…...
代码随想录算法训练营第四十三天| 动态规划06
322. 零钱兑换 如果求组合数就是外层for循环遍历物品,内层for遍历背包。 如果求排列数就是外层for遍历背包,内层for循环遍历物品。 这句话结合本题 大家要好好理解。 视频讲解:动态规划之完全背包,装满背包最少的物品件数是多少&…...

UI自动化教程 —— 元素定位技巧:精确找到你需要的页面元素
引言 在UI自动化测试中,准确地定位页面元素是至关重要的。无论是点击按钮、填写表单还是验证页面内容,都需要首先找到相应的页面元素。Playwright 提供了多种方法来实现这一点,包括使用CSS选择器和XPath进行元素定位,以及利用文本…...
MySQL六大日志的功能介绍。
前言 首先,MySQL的日志应该包括二进制日志(Binary Log)、错误日志(Error Log)、查询日志(General Query Log)、慢查询日志(Slow Query Log)、重做日志(Redo …...
二级指针略解【C语言】
以int** a为例 1.二级指针的声明 a 是一个指向 int*(指向整型的指针)的指针,即二级指针。 通俗的讲,a是一个指向指针的指针,对a解引用会是一个指针。 它可以用于操作动态分配的二维数组、指针数组或需要间接修改指针…...
鸿蒙状态管理概述
状态管理 状态管理之v1LocalStorageLocalStorageLink的框架行为LocalStorageProp的框架行为LocalStorage使用场景 AppStorageStorageLink的框架行为StorageProp的框架行为AppStorage的使用场景 PersistentStorageEnvironmentEnvironment内置参数 WatchWatch的使用场景 $$语法$$…...

【核心算法篇十三】《DeepSeek自监督学习:图像补全预训练方案》
引言:为什么自监督学习成为AI新宠? 在传统监督学习需要海量标注数据的困境下,自监督学习(Self-Supervised Learning)凭借无需人工标注的特性异军突起。想象一下,如果AI能像人类一样通过观察世界自我学习——这正是DeepSeek图像补全方案的技术哲学。根据,自监督学习通过…...

由浅入深学习大语言模型RLHF(PPO强化学习- v1浅浅的)
最近,随着DeepSeek的爆火,GRPO也走进了视野中。为了更好的学习GRPO,需要对PPO的强化学习有一个深入的理解,那么写一篇文章加深理解吧。纵观网上的文章,要么说PPO原理,各种复杂的公式看了就晕,要…...

网络安全三件套
一、在线安全的四个误解 Internet实际上是个有来有往的世界,你可以很轻松地连接到你喜爱的站点,而其他人,例如黑客也很方便地连接到你的机器。实际上,很多机器都因为自己很糟糕的在线安全设置无意间在机器和系统中留下了“…...

瑞芯微RV1126部署YOLOv8全流程:环境搭建、pt-onnx-rknn模型转换、C++推理代码、错误解决、优化、交叉编译第三方库
目录 1 环境搭建 2 交叉编译opencv 3 模型训练 4 模型转换 4.1 pt模型转onnx模型 4.2 onnx模型转rknn模型 4.2.1 安装rknn-toolkit 4.2.2 onn转成rknn模型 5 升级npu驱动 6 C++推理源码demo 6.1 原版demo 6.2 增加opencv读取图片的代码 7 交叉编译x264 ffmepg和op…...
【ISO 14229-1:2023 UDS诊断(会话控制0x10服务)测试用例CAPL代码全解析⑤】
ISO 14229-1:2023 UDS诊断【会话控制0x10服务】_TestCase05 作者:车端域控测试工程师 更新日期:2025年02月15日 关键词:UDS诊断、0x10服务、诊断会话控制、ECU测试、ISO 14229-1:2023 TC10-005测试用例 用例ID测试场景验证要点参考条款预期…...

python-leetcode 35.二叉树的中序遍历
给定一个二叉树的根节点root,返回它的中序遍历。 方法一:递归 二叉树的中序遍历:按照访问左子树——根节点——右子树的方式遍历这棵树,而在访问左子树或者右子树的时候我们按照同样的方式遍历,直到遍历完整棵树。因此整个遍历过…...
glob 用法技巧
目录 处理大量文件节省内存 匹配多个文件扩展名 遍历多种格式文件 遍历某一个文件: 查找当前目录和子目录 6. 排除特定文件 7. 大小写不敏感匹配 8. 获取绝对路径 9. 处理特殊字符 处理大量文件节省内存 技巧:用 iglob 替代 glob,逐…...
CodeGPT 使用教程(适用于 VSCode)
CodeGPT 使用教程(适用于 VSCode) CodeGPT 是一个 VSCode 插件,可以让你在代码编辑器中直接调用 GPT 进行代码补全、优化、调试等操作。以下是详细的安装和使用步骤: 1. 安装 CodeGPT 方式 1:从 VSCode 插件市场安装…...
以下是MySQL中常见的增删改查语句
以下是MySQL中常见的增删改查语句: 增加数据(INSERT) 基本语法: INSERT INTO table_name (column1, column2,...) VALUES (value1, value2,...); 示例:向名为 students 的表中插入一条学生记录, id 为1&am…...
Vue3 与 TypeScript 实战:核心细节与最佳实践
引言 Vue3 的 Composition API 与 TypeScript 的强类型支持完美契合,极大提升了代码的可维护性和开发体验。本文将深入探讨 Vue3 TypeScript 的关键细节,并通过实际代码示例展示如何高效结合二者。 一、组合式 API 与类型推导 Vue3 的 setup 函数与 T…...
23种设计模式 - 解释器模式
模式定义 解释器模式(Interpreter Pattern)是一种行为型设计模式,用于为特定语言(如数控系统的G代码)定义文法规则,并构建解释器来解析和执行该语言的语句。它通过将语法规则分解为多个类,实现…...
常用的 React Hooks 的介绍和示例
目录 1. useState2. useEffect3. useContext4. useReducer5. useCallback6. useMemo7. useRef8. useImperativeHandle9. useLayoutEffect10. useDebugValue 常用的 React Hooks 的介绍和示例: 1. useState useState 是一个用于在函数组件中添加状态的 Hook。 impo…...

基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...

现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...

【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...
Qt 事件处理中 return 的深入解析
Qt 事件处理中 return 的深入解析 在 Qt 事件处理中,return 语句的使用是另一个关键概念,它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别:不同层级的事件处理 方…...