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…...
为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
DBLP数据库是什么?
DBLP(Digital Bibliography & Library Project)Computer Science Bibliography是全球著名的计算机科学出版物的开放书目数据库。DBLP所收录的期刊和会议论文质量较高,数据库文献更新速度很快,很好地反映了国际计算机科学学术研…...
WEB3全栈开发——面试专业技能点P4数据库
一、mysql2 原生驱动及其连接机制 概念介绍 mysql2 是 Node.js 环境中广泛使用的 MySQL 客户端库,基于 mysql 库改进而来,具有更好的性能、Promise 支持、流式查询、二进制数据处理能力等。 主要特点: 支持 Promise / async-await…...
MeshGPT 笔记
[2311.15475] MeshGPT: Generating Triangle Meshes with Decoder-Only Transformers https://library.scholarcy.com/try 真正意义上的AI生成三维模型MESHGPT来袭!_哔哩哔哩_bilibili GitHub - lucidrains/meshgpt-pytorch: Implementation of MeshGPT, SOTA Me…...
二叉树-144.二叉树的前序遍历-力扣(LeetCode)
一、题目解析 对于递归方法的前序遍历十分简单,但对于一位合格的程序猿而言,需要掌握将递归转化为非递归的能力,毕竟递归调用的时候会调用大量的栈帧,存在栈溢出风险。 二、算法原理 递归调用本质是系统建立栈帧,而非…...
