新版Spring Security6.2架构 (一)
Spring Security
新版springboot 3.2已经集成Spring Security 6.2,和以前会有一些变化,本文主要针对官网的文档进行一些个人翻译和个人理解,不对地方请指正。
整体架构
Spring Security的Servlet 支持是基于Servelet过滤器,如下图所示,当http请求到来时候,会经历下面的过滤器。
客户端发送请求到应用程序,容器会生成过滤链(其中包含过滤器实例)并且会根据URL地址处理httpServletRequest。在Spring MVC应用程序中,Servlet是DispatcherServlet的实例,一个Servlet最多可以处理一个HttpServletRequest 和 HttpServletResponse. 但是使用的过滤器不止一个,主要有以下两点:
1.防止下游过滤器实例或者Servlet被调用,这种情况下,过滤器通常会写入HttpServletResponse.
2.修改HttpServletRequest 和 HttpServletRespons,并且传递给下游过滤器和Servlet.
过滤器强大在于传递给它的过滤器链。
过滤器链的例子:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {// do something before the rest of the applicationchain.doFilter(request, response); // invoke the rest of the application// do something after the rest of the application
}
官网这段话,其实看这个图或者说和servlet的过滤器执行顺序相关,图里所示就是Filter0->Filter1->Filter2->Servlet,所以使用过滤器是多个的,并且要按照过滤器顺序往下传递,每个过滤器最重要是重写doFilter, 并且这个函数参数有过滤器链FilterChain,每个过滤器在做完自己事情(do something before)后,就得传递给过滤器链,chain.doFilter,保证往下传递。相反,回来的时候肯定相反方向:Servelet->Filter2->Filter1->Filter0。
DelegatingFilterProxy
Spring提供了一个过滤器叫DelegatingFilterProxy,它链接了Servlet容器的生命周期和Spring applicationContext。Servlet容器有自己的标准,可以注册Filter过滤器实例,但是它没有办法找到Spring所定义的Beans对象。你可以注册DelegatingFilterProxy,并且它符合Servlet容器机制,还能委托给Spring Bean所实现的Filter过滤器。
DelegatingFilterProxy会从AllicationContext查找Bean Filter0并且调用Bean Filter0过滤器
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {Filter delegate = getFilterBean(someBeanName); //1delegate.doFilter(request, response); //2
}
1.惰性获取已经注册Spring Bean的Filter
2.将工作委托给Spring Bean
DelegatingFilterProxy另个好处就是允许延迟查询Filter实例。这个是很重要的,因为容器需要在容器启动前注册Filter实例,但是,Spring 通常使用 ContextLoaderListener 来加载 Spring Bean,直到需要注册 Filter 实例之后才会执行此操作。
FilterChainProxy
Spring Security的Servelet 支持都被包含在了FilterChainProxy,FilterChainProxy是一个特殊过滤器,能够通过SecurityFilterChain委托给许多Filter过滤器实例,FilterChainProxy是一个bean,所以通常被包装在DelegatingFilterProxy中。
SecurityFilterChain
SecurityFilterChain被FilterChainProxy使用,并且决定了哪个Spring Security过滤器实例对当前请求进行调用
SecurityFilterChain 中的SecurityFilter通常是 Bean,但它们是使用 FilterChainProxy 而不是 DelegatingFilterProxy 注册的。FilterChainProxy 为直接向 Servlet 容器或 DelegatingFilterProxy 注册提供了许多优势。首先,它为 Spring Security 的所有 Servlet 支持提供了一个起点。因此,如果您尝试对 Spring Security 的 Servlet 支持进行故障排除,在 FilterChainProxy 中添加调试点是一个很好的起点。
其次,由于 FilterChainProxy 是 Spring Security 使用的核心,因此它可以执行不被视为可选的任务。例如,它会清除 SecurityContext 以避免内存泄漏。它还应用 Spring Security 的 HttpFirewall 来保护应用程序免受某些类型的攻击。
此外,它还在确定何时应调用 SecurityFilterChain 方面提供了更大的灵活性。在 Servlet 容器中,仅根据 URL 调用过滤器实例。但是,FilterChainProxy 可以使用 RequestMatcher 接口根据 HttpServletRequest 中的任何内容确定调用。
在多个 SecurityFilterChain 图中,FilterChainProxy 决定应使用哪个 SecurityFilterChain。仅调用匹配的第一个 SecurityFilterChain。如果请求 /api/messages/ 的 URL,则它首先与 /api/** 的 SecurityFilterChain0 模式匹配,因此仅调用 SecurityFilterChain0,即使它在 SecurityFilterChainn 上也匹配。如果请求 /messages/ 的 URL,则它与 /api/** 的 SecurityFilterChain0 模式不匹配,因此 FilterChainProxy 会继续尝试每个 SecurityFilterChain。假设没有其他 SecurityFilterChain 实例匹配,则调用 SecurityFilterChaann。
请注意,SecurityFilterChain0 只配置了三个security Filter实例。但是,SecurityFilterChainn 配置了四个安全筛选器实例。需要注意的是,每个 SecurityFilterChain 都可以是唯一的,并且可以单独配置。事实上,如果应用程序希望 Spring Security 忽略某些请求,则 SecurityFilterChain 可能具有零安全过滤器实例。
SecurityFilter
security Filters使用 SecurityFilterChain API 插入到 FilterChainProxy 中。这些过滤器可用于多种不同的目的,例如身份验证、授权、漏洞利用保护等。过滤器按特定顺序执行,以保证在正确的时间调用它们,例如,执行身份验证的过滤器应先调用,然后再调用执行授权的过滤器。通常没有必要知道 Spring Security 过滤器的顺序。但是,有时了解排序是有益的,如果您想了解它们,可以查看 FilterOrderRegistration 代码。
为了举例说明上述段落,让我们考虑以下安全配置:
@Configuration
@EnableWebSecurity
public class SecurityConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.csrf(Customizer.withDefaults()).authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated()).httpBasic(Customizer.withDefaults()).formLogin(Customizer.withDefaults());return http.build();}}
上面的配置就会导致如下的过滤器的顺序
Filter | Added by |
---|---|
CsrfFilter |
|
UsernamePasswordAuthenticationFilter |
|
BasicAuthenticationFilter |
|
AuthorizationFilter |
|
1.调用 CsrfFilter 来防止 CSRF 攻击。
2.调用身份验证筛选器来对请求进行身份验证。
3.调用 AuthorizationFilter 来授权请求。
添加自定义的过滤器
大多数情况下,默认安全筛选器足以为应用程序提供安全性。但是,有时您可能希望将自定义筛选器添加到安全筛选器链。
例如,假设你要添加一个获取租户 ID 标头的筛选器,并检查当前用户是否有权访问该租户。前面的描述已经为我们提供了在何处添加过滤器的线索,因为我们需要知道当前用户,因此我们需要在身份验证过滤器之后添加它。
首先,让我们创建过滤器:
public class TenantFilter implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;String tenantId = request.getHeader("X-Tenant-Id"); //1boolean hasAccess = isUserAllowed(tenantId); //2if (hasAccess) {filterChain.doFilter(request, response); //3return;}throw new AccessDeniedException("Access denied"); //4 }}
面的示例代码执行以下操作:
1.从请求标头中获取租户 ID。
2.检查当前用户是否有权访问租户 ID。
3.如果用户具有访问权限,则调用链中的其余筛选器。
4.如果用户没有访问权限,则引发 AccessDeniedException。
官网还给了一个建议,可以从 OncePerRequestFilter 进行扩展,而不是实现 Filter,OncePerRequestFilter 是每个请求仅调用一次的过滤器的基类,并提供带有 HttpServletRequest 和 HttpServletResponse 参数的 doFilterInternal 方法。
这个建议,主要是因为servlet filter因为版本不同,以及不同web container,一个请求不一定只过一个fitler,像servlet2.3中,Filter会经过一切请求,包括服务器内部使用forward.
最后在把上面自定义的过滤器加到过滤器链中.
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http// ....addFilterBefore(new TenantFilter(), AuthorizationFilter.class); return http.build();
}
HttpSecurity会通过addFilterBefore在AuthorizationFilter前添加TenanFilter.
通过在 AuthorizationFilter 之前添加过滤器,我们可以确保在身份验证过滤器之后调用 TenantFilter。您还可以使用 HttpSecurity的addFilterAfter 在特定过滤器之后添加过滤器,或使用 HttpSecurity的addFilterAt 在过滤器链中的特定过滤器位置添加过滤器。
将过滤器声明为 Spring Bean 时要小心,不管用 @Component 注解或在配置中将其声明为 Bean,因为 Spring Boot 会自动将其注册到嵌入式容器中。这可能会导致过滤器被调用两次,一次由容器调用,一次由 Spring Security 调用,并且顺序不同。
例如,如果您仍然希望将过滤器声明为 Spring Bean 以利用依赖注入,并避免重复调用,您可以通过声明 FilterRegistrationBean bean 并将其 enabled 属性设置为 false 来告诉 Spring Boot 不要将其注册到容器中:
@Bean
public FilterRegistrationBean<TenantFilter> tenantFilterRegistration(TenantFilter filter) {FilterRegistrationBean<TenantFilter> registration = new FilterRegistrationBean<>(filter);registration.setEnabled(false);return registration;
}
处理异常
ExceptionTranslationFilter 允许将 AccessDeniedException 和 AuthenticationException 转换为 HTTP 响应。
ExceptionTranslationFilter 作为安全过滤器之一插入到 FilterChainProxy 中。
1.ExceptionTranslationFilter 调用 FilterChain.doFilter(request, response) 来调用应用程序的其余部分。
2.如果用户未通过身份验证或它是 AuthenticationException,则启动Authentication。(1)SecurityContextHolder 被清除。
(2)保存 HttpServletRequest,以便在Authentication验证成功后使用它来重播原始请求。
(3)AuthenticationEntryPoint 用于从客户端请求凭据。例如,它可能会重定向到登录页面或发送 WWW-Authenticate 标头。
3.否则,如果它是 AccessDeniedException,则拒绝访问。调用 AccessDeniedHandler 来处理被拒绝的访问。
如果应用程序未引发 AccessDeniedException 或 AuthenticationException,则 ExceptionTranslationFilter 不会执行任何操作。
try {filterChain.doFilter(request, response);
} catch (AccessDeniedException | AuthenticationException ex) {if (!authenticated || ex instanceof AuthenticationException) {startAuthentication(); } else {accessDenied(); }
}
1. 调用 FilterChain.doFilter(request, response) 等同于调用应用程序的其余部分。这意味着,如果应用程序的另一部分(FilterSecurityInterceptor 或方法安全性)引发 AuthenticationException 或 AccessDeniedException,则会在此处捕获并处理它。
2.如果用户未经过身份验证或是 AuthenticationException,则启动身份验证。
3.否则,访问被拒绝
在Authentication中保持请求
如处理安全异常中所示,当请求没有身份验证并且针对需要身份验证的资源时,需要保存身份验证资源的请求,以便在身份验证成功后重新请求。在 Spring Security 中,这是通过使用 RequestCache 实现保存 HttpServletRequest 来完成的。
RequestCache
HttpServletRequest 保存在 RequestCache 中。当用户成功进行身份验证时,RequestCache 将用于重播原始请求。RequestCacheAwareFilter 是使用 RequestCache 保存 HttpServletRequest 的。
默认情况下,使用 HttpSessionRequestCache。下面的代码演示如何自定义 RequestCache 实现,该实现用于检查 HttpSession 中是否存在名为 continue 的参数时已保存的请求。
@Bean
DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {HttpSessionRequestCache requestCache = new HttpSessionRequestCache();requestCache.setMatchingRequestParameterName("continue");http// ....requestCache((cache) -> cache.requestCache(requestCache));return http.build();
}
防止请求被保存
您可能希望不要在会话中存储用户未经身份验证的请求,原因有很多。您可能希望将该存储卸载到用户的浏览器上,或将其存储在数据库中。或者,您可能希望关闭此功能,因为您始终希望将用户重定向到主页,而不是他们在登录前尝试访问的页面。
为此,可以使用 NullRequestCache 实现。
@Bean
SecurityFilterChain springSecurity(HttpSecurity http) throws Exception {RequestCache nullRequestCache = new NullRequestCache();http// ....requestCache((cache) -> cache.requestCache(nullRequestCache));return http.build();
}
Spring Boot Security Get started
@EnableWebSecurity //1
@Configuration
public class DefaultSecurityConfig {@Bean@ConditionalOnMissingBean(UserDetailsService.class)InMemoryUserDetailsManager inMemoryUserDetailsManager() { //2String generatedPassword = // ...;return new InMemoryUserDetailsManager(User.withUsername("user").password(generatedPassword).roles("ROLE_USER").build());}@Bean@ConditionalOnMissingBean(AuthenticationEventPublisher.class)DefaultAuthenticationEventPublisher defaultAuthenticationEventPublisher(ApplicationEventPublisher delegate) { //3return new DefaultAuthenticationEventPublisher(delegate);}
}
1.使用EnableWebSecurity注册Spring Security's 注册默认的过滤链(default filter chain)对应的bean
2.第二个就是注册一个UserDetailsService默认bean,现在注册是默认的内存里面用户和密码
3.注册一个authenticationEventPublisher的bean对象给authentication 事件
相关文章:

新版Spring Security6.2架构 (一)
Spring Security 新版springboot 3.2已经集成Spring Security 6.2,和以前会有一些变化,本文主要针对官网的文档进行一些个人翻译和个人理解,不对地方请指正。 整体架构 Spring Security的Servlet 支持是基于Servelet过滤器,如下…...
名字的漂亮度
给出一个字符串,该字符串仅由小写字母组成,定义这个字符串的“漂亮度”是其所有字母“漂亮度”的总和。 每个字母都有一个“漂亮度”,范围在1到26之间。没有任何两个不同字母拥有相同的“漂亮度”。字母忽略大小写。给出多个字符串࿰…...

机器学习基本概念2
资料来源: https://www.youtube.com/watch?vYe018rCVvOo&listPLJV_el3uVTsMhtt7_Y6sgTHGHp1Vb2P2J&index1 https://www.youtube.com/watch?vbHcJCp2Fyxs&listPLJV_el3uVTsMhtt7_Y6sgTHGHp1Vb2P2J&index2 分三步 1、 定义function b和w是需要透…...
Spring Cloud 与微服务学习总结(19)—— Spring Cloud Alibaba 之 Nacos 2.3.0 史上最大更新版本发布
Nacos 一个用于构建云原生应用的动态服务发现、配置管理和服务管理平台,由阿里巴巴开源,致力于发现、配置和管理微服务。说白了,Nacos 就是充当微服务中的的注册中心和配置中心。 Nacos 2.3.0 新特性 1. 反脆弱插件 Nacos 2.2.0 版本开始加入反脆弱插件,从 2.3.0 版本开…...
八、C#笔记
/// <summary> /// 第十三章:创建接口和定义抽象类 /// </summary> namespace Chapter13 { class Program { static void Main(string[] args) { //13.1理解接口 ///13.1.1定义接口 ///…...

利用Node.js和cpolar实现远程访问,无需公网IP和路由器设置的完美解决方案
文章目录 前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5.固定公网地址 前言 Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation࿰…...
C++如何通过调用ffmpeg接口对H264文件进行编码和解码
C可以通过调用FFmpeg的API来对H264文件进行编码和解码。下面是一个简单的例子。 首先需要在代码中包含FFmpeg的头文件: extern "C" { #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libswscale/swscale…...

使用MetaMask + Ganache搭建本地私有网络并实现合约部署与互动
我使用Remix编写合约,MetaMask钱包工具和Ganache搭建了一个私有网络,并且实现了合约的部署和互动。 在前面的博客中提到了 Remix在线环境及钱包申请 以及 Solidity的基本语法 ,没看过的小伙伴可以点击链接查看一下,都是在本专栏下…...

目标检测、目标跟踪、重识别
文章目录 环境前言项目复现特征提取工程下载参考资料 环境 ubuntu 18.04 64位yolov5deepsortfastreid 前言 基于YOLOv5和DeepSort的目标跟踪 介绍过针对行人的检测与跟踪。本文介绍另一个项目,结合 FastReid 来实现行人的检测、跟踪和重识别。作者给出的2个主…...
高防IP防御效果怎么样,和VPN有区别吗
高防IP主要是用于防御网络攻击,可以抵御各种类型的DDoS攻击,隐藏源IP地址,提高网络安全性和用户体验。主要目的是解决外部网络攻击问题,保护网络安全,避免因攻击而导致的业务中断和数据泄露等问题。 而VPN则是一种可以…...
探秘MSSQL存储过程:功能、用法及实战案例
在现代软件开发中,高效地操作数据库是至关重要的。而MSSQL(Microsoft SQL Server)作为一款强大的关系型数据库管理系统,为我们提供了丰富的功能和工具来处理数据。其中,MSSQL存储过程是一项强大而又常用的功能…...

我们常说的流应用到底是什么?
流应用是DCloud公司开发的一种可以让手机App安装包实现边用边下的技术。基于HTML5规范的即点即用应用,开发者按照HTML5规范开发的应用,可以在支持HTML5流应用的发行渠道实现即点即用的效果。 流应用是基于 HTML5规范的即点即用应用,开发者按照…...

ELK 日志解决方案
ELK 是目前最流行的集中式日志解决方案,提供了对日志收集、存储、展示等一站式的解决方案。 ELK 分别指 Elasticsearch、Logstash、Kibana。 Elasticsearch:分布式数据搜索引擎,基于 Apache Lucene 实现,可集群,提供…...

本项目基于Spring boot的AMQP模块,整合流行的开源消息队列中间件rabbitMQ,实现一个向rabbitMQ
在业务逻辑的异步处理,系统解耦,分布式通信以及控制高并发的场景下,消息队列有着广泛的应用。本项目基于Spring的AMQP模块,整合流行的开源消息队列中间件rabbitMQ,实现一个向rabbitMQ添加和读取消息的功能。并比较了两种模式&…...

freeswitch webrtc video_demo客户端进行MCU的视频会议
系统环境 一、编译服务器和加载模块 二、下载编译指定版本video_demo 三、配置verto.conf.xml 1.修改配置文件 2.重新启动 四、MCU通话测试 1.如何使用video_demo 2.测试结果 五、MCU的通话原理及音频/视频/布局/管理员等参数配置 附录 freeswitch微信交流群 系统环境 lsb_rel…...
【鸿蒙学习网络】
鸿蒙技术学习相关学习资料 官方文档:华为官方提供了鸿蒙开发者文档,包括开发指南、API参考和示例代码等。您可以访问华为开发者中心网站(https://developer.harmonyos.com/)获取最新的官方文档和教程。在 线 课 程 : …...

MySQL系列(一):索引篇
为什么是B树? 我们推导下,首先看下用哈希表做索引,是否可以满足需求。如果我们用哈希建了索引,那么对于如下这种SQL,通过哈希,可以快速检索出数据: select * from t_user_info where id1;但是这…...

Flink Flink数据写入Kafka
一、环境准备 官网地址 flink官方集成了通用的 Kafka 连接器,使用时需要根据生产环境的版本引入相应的依赖 <properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><flink.version>1.14.6</flink.version&g…...

《论文阅读》用于情绪回复生成的情绪正则化条件变分自动编码器 Affective Computing 2021
《论文阅读》用于情绪回复生成的情绪正则化条件变分自动编码器 前言简介模型结构实验结果总结前言 今天为大家带来的是《Emotion-Regularized Conditional Variational Autoencoder for Emotional Response Generation》 出版:IEEE Transactions on Affective Computing 时间…...

Pytorch CIFAR10图像分类 Swin Transformer篇
Pytorch CIFAR10图像分类 Swin Transformer篇 文章目录 Pytorch CIFAR10图像分类 Swin Transformer篇4. 定义网络(Swin Transformer)Swin Transformer整体架构Patch MergingW-MSASW-MSARelative position biasSwin Transformer 网络结构Patch EmbeddingP…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...

多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...

循环语句之while
While语句包括一个循环条件和一段代码块,只要条件为真,就不断 循环执行代码块。 1 2 3 while (条件) { 语句 ; } var i 0; while (i < 100) {console.log(i 当前为: i); i i 1; } 下面的例子是一个无限循环,因…...

数据可视化交互
目录 【实验目的】 【实验原理】 【实验环境】 【实验步骤】 一、安装 pyecharts 二、下载数据 三、实验任务 实验 1:AQI 横向对比条形图 代码说明: 运行结果: 实验 2:AQI 等级分布饼图 实验 3:多城市 AQI…...

运动控制--BLDC电机
一、电机的分类 按照供电电源 1.直流电机 1.1 有刷直流电机(BDC) 通过电刷与换向器实现电流方向切换,典型应用于电动工具、玩具等 1.2 无刷直流电机(BLDC) 电子换向替代机械电刷,具有高可靠性,常用于无人机、高端家电…...
Ansible+Zabbix-agent2快速实现对多主机监控
ansible Ansible 是一款开源的自动化工具,用于配置管理(Configuration Management)、应用部署(Application Deployment)、任务自动化(Task Automation)和编排(Orchestration…...

可视化图解算法48:有效括号序列
牛客网 面试笔试 TOP101 | LeetCode 20. 有效的括号 1. 题目 描述 给出一个仅包含字符(,),{,},[和],的字符串,判断给出的字符串是否是合法的括号序列 括号必须以正确的顺序关闭,"()"和"()[]{}"都是合法的括号序列&…...

Flask与Celery 项目应用(shared_task使用)
目录 1. 项目概述主要功能技术栈 2. 项目结构3. 环境设置创建虚拟环境并安装依赖主要依赖 4. 应用配置Flask应用初始化 (__init__.py)Celery应用初始化 (make_celery.py) 5. 定义Celery任务 (tasks.py)任务说明 6. 创建API端点 (views.py)API端点说明 7. 前端界面 (index.html)…...