Spring Security总体架构介绍

news2024/11/18 7:44:10

参考:架构 :: Spring Security Reference (springdoc.cn)

一、过滤器

Spring Security 框架对 Servlet 请求的处理是基于过滤器机制。

容器会提前创建好FilterChain对每一个请求进行过滤,FilterChain中包含Filter 实例和 Servlet(Spring MVC应用程序中,是 DispatcherServlet 实例。)。

Filter对于一个请求,可以完成如下操作(一个 Filter 只会影响其下游的 Filter 实例和 Servlet,所以每个 Filter 的调用顺序是非常重要的。):

  • 终止请求:Filter 通常会使用 HttpServletResponse 对客户端写入响应。
  • 修改请求:修改下游的 Filter 实例和 Servlet 接收到的 HttpServletRequest 或 HttpServletResponse。

一个过滤器的过滤函数结构:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
    // do something before the rest of the application
    chain.doFilter(request, response); // invoke the rest of the application
    // do something after the rest of the application
}

二、DelegatingFilterProxy

Servlet容器会使用自己的标准来注册 Filter 实例,这不同于Spring框架的IOC功能;也就是说,Servlet容器可能不会使用Spring的ApplicationContext(Bean容器)。

DelegatingFilterProxy的作用就是在 Servlet 容器的生命周期和 Spring 的 ApplicationContext 之间建立桥梁。

虽然Servlet容器不直接了解或管理由Spring框架定义的Bean,但是通过在Servlet容器中注册DelegatingFilterProxy这个过滤器(该过滤器会将所有的工作委托给Spring应用程序上下文中定义的具体的Filter实现的Spring Bean),过滤器的逻辑实际上是由Spring Bean来执行的,而不是由Servlet容器直接执行的。

DelegatingFilterProxy 从 ApplicationContext 查找过滤器的Bean,然后调用他们。

DelegatingFilterProxy的过滤函数伪代码:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
    Filter delegate = getFilterBean(someBeanName);//获得一个过滤器的Bean
    delegate.doFilter(request, response);//调用该过滤器
}

DelegatingFilterProxy还有一个优点,是允许延迟查找 Filter Bean实例:在Servlet容器启动之前,容器需要提前注册 Filter 实例;然而, Spring 通常使用 ContextLoaderListener 来加载 Spring Bean,这在Servlet容器注册完 Filter 实例之后才会完成,导致过滤器都是无法使用Bean容器中的Bean的(因为Bean还未创建,过滤器创建完成后内部的Bean引用都是空指针);现在通过DelegatingFilterProxy,就可以让Bean Filter使用Bean容器中的Bean了。

三、FilterChainProxy与SecurityFilterChain

FilterChainProxy 是一个 Bean,通常被包裹在 DelegatingFilterProxy 中。

FilterChainProxy 是 Spring Security 提供的一个特殊的 Filter,允许将请求通过 SecurityFilterChain 委托给许多 Filter 实例。

SecurityFilterChain 被 FilterChainProxy 用来确定当前请求应该调用哪些 Spring Security Filter 实例。

(可以将 FilterChainProxy 看做是 SecurityFilterChain 的带逻辑选择的hashset集合,而 SecurityFilterChain 又是 SecurityFilter 的顺序list集合。每个 SecurityFilterChain 都可以是唯一的,并且可以单独配置。)

单个Security Filter Chain示意图:

多个Security Filter Chain示意图:

在该多个Security Filter Chain的示意图中:

  • 如果请求的URL是 /api/messages/,它首先与 /api/** 的 SecurityFilterChain0 模式匹配,所以只有 SecurityFilterChain_0 被调用,尽管它也与 SecurityFilterChain_n 匹配。
  • 如果请求的URL是 /messages/,它与 /api/** 的 SecurityFilterChain_0 模式不匹配,所以 FilterChainProxy 继续尝试每个 SecurityFilterChain。假设没有其他 SecurityFilterChain 实例相匹配,则调用 SecurityFilterChain_n。

与直接向Servlet容器或 DelegatingFilterProxy 注册Security Filter 的 Bean 相比,向 FilterChainProxy 注册有很多优势:

  • 提供起点进行故障诊断:FilterChainProxy 为 Spring Security 的所有 Servlet 支持提供了一个起点。如果试图对 Spring Security 的 Servlet 支持进行故障诊断,在 FilterChainProxy 中添加一个调试点是一个很好的开始。
  • 执行一些关键任务:作为核心组件,FilterChainProxy 可以执行对应用程序的安全性和稳定性至关重要的任务。 例如,它清除了 SecurityContext 以避免内存泄漏。它还应用Spring Security的 HttpFirewall 来保护应用程序免受某些类型的攻击。
  • 提供更大的灵活性:在Servlet容器中,Filter 实例仅基于URL被调用。 然而,FilterChainProxy 可以通过使用 RequestMatcher 接口,根据 HttpServletRequest 中的任何内容确定调用。

四、Security Filter

Security Filter 通过 SecurityFilterChain API 插入 FilterChainProxy 中。

举例:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(Customizer.withDefaults())
            .authorizeHttpRequests(authorize -> authorize
                .anyRequest().authenticated()
            )
            .httpBasic(Customizer.withDefaults())
            .formLogin(Customizer.withDefaults());
        return http.build();
    }

}

过滤器顺序:

  1. CsrfFilter:HttpSecurity.csrf。CSRF过滤器,用于检查每个请求中是否存在CSRF令牌,并根据需要拦截或处理请求,以防止CSRF攻击。
  2. UsernamePasswordAuthenticationFilter:HttpSecurity.formLogin。表单登录过滤器,用于支持基于表单的用户身份验证,通常用于显示登录页面并接受用户凭据进行验证。
  3. BasicAuthenticationFilter:HttpSecurity.httpBasic。HTTP基本认证过滤器,用于支持基本的HTTP身份验证机制,当需要对请求进行身份验证时,它会提示用户提供用户名和密码进行认证。
  4. AuthorizationFilter:HttpSecurity.authorizeHttpRequests。授权过滤器用于验证对每个HTTP请求的访问权限,确保只有经过身份验证的用户才能访问受保护的资源。

可以看出过滤器顺序与声明顺序无关,具体的顺序由FilterOrderRegistration类决定。(源码为https://github.com/spring-projects/spring-security/tree/main/config/src/main/java/org/springframework/security/config/annotation/web/builders/FilterOrderRegistration.java)

项目启动时会打印每个SecurityFilterChain所有的过滤器:

添加过滤器

自定义Filter的框架:

public class MyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws Exception {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        if(XXX){
            throw new AccessDeniedException("");
        }else if(XXX){
            throw new AuthenticationException("");
        }
        // do something before the rest of the application
        filterChain.doFilter(request, response); // invoke the rest of the application
        // do something after the rest of the application
    }
}

在自定义一个过滤器后,可在注册SecurityFilterChain为Bean的函数中添加该过滤器,如:

@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        // ...
        .addFilterBefore(new MyFilter(), AuthorizationFilter.class); 
    return http.build();
}

示例中的代码将自定义过滤器添加到 AuthorizationFilter 之前。也可以使用addFilterAfter 添加到某个特定的 filter 之后或使用 addFilterAt 将 filter 添加到 filter chain 中的某个位置。

注意:如果将过滤器声明为Bean,可能Springboot会自动将过滤器注册到Servlet容器中,导致一个请求会在Serlvet容器的过滤器中过滤一次,又在DelegatingFilterProxy中再过滤一次,为了避免这个问题,我们可以不使用@Bean、@Component,或声明 FilterRegistrationBean Bean 并将其 enabled 属性设置为 false 来告诉 Spring Boot 不要向容器注册它:

@Bean
public FilterRegistrationBean<TenantFilter> tenantFilterRegistration(TenantFilter filter) {
    FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>(filter);
    registration.setEnabled(false);
    return registration;
}

五、ExceptionTranslationFilter:异常处理

ExceptionTranslationFilter 允许将 AccessDeniedException 和 AuthenticationException 翻译成 HTTP 响应。

ExceptionTranslationFilter 作为 Security Filter 之一被插入到 FilterChainProxy 中。

直接看不是很清晰,先看伪代码:

try {
    filterChain.doFilter(request, response);
} catch (AccessDeniedException | AuthenticationException ex) {
    if (!authenticated || ex instanceof AuthenticationException) {
        startAuthentication();
    } else {
        accessDenied();
    }
}
  1. 首先,ExceptionTranslationFilter 调用 FilterChain.doFilter(request, response) 来调用应用程序的其他部分。
  2. 如果用户没有被认证,或者是一个 AuthenticationException,那么就 开始认证(startAuthentication())。
  3. 否则,如果是 AccessDeniedException,那么就是 Access Denied(没有授权)。 AccessDeniedHandler 被调用来处理拒绝访问(access denied)。

六、RequestCache:保存认证之间的请求

在Spring Security中,请求缓存用于在用户进行身份验证之前保存用户的原始请求,以便在身份验证成功后将用户重定向回原始请求。这对于避免在登录后用户被重定向到应用程序的默认页面而失去原始请求非常有用。

当一个请求没有认证,会进入认证流程;当认证完成后,RequestCache 被用来重放原始请求。

默认情况下,使用一个 HttpSessionRequestCache 来保存 HttpServletRequest。

在SecurityFilterChain中定制RequestCache:

@Bean
DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
    HttpSessionRequestCache requestCache = new HttpSessionRequestCache();
    requestCache.setMatchingRequestParameterName("continue");//自定义请求缓存的匹配参数名称,默认使用j_spring_security_request,Spring Security会将原始请求保存在名为 "continue" 的请求参数中。
    http
        // ...
        .requestCache((cache) -> cache
            .requestCache(requestCache)
        );
    return http.build();
}

关闭RequestCache:

RequestCache nullRequestCache = new NullRequestCache();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1117566.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

编写后台登录滑动成功获取验证码 人机验证

vue-puzzle-vcode Vue 纯前端的拼图人机验证、右滑拼图验证 安装vue-puzzle-vcode npm install vue-puzzle-vcode --save使用vue-puzzle-vcode import Vcode from "vue-puzzle-vcode";<Vcode :show"isShow" success"onSuccess" close"…

ZooKeeper+HBase分布式集群环境搭建

安装版本&#xff1a;hadoop-2.10.1、zookeeper-3.4.12、hbase-2.3.1 一、zookeeper集群搭建与配置 1.下载zookeeper安装包 2.解压移动zookeeper 3.修改配置文件&#xff08;创建文件夹&#xff09; 4.进入conf/ 5.修改zoo.cfg文件 6.进入/usr/local/zookeeper-3.4.12/zkdat…

虚拟机与主机(win10之间的通信)

(201条消息) Ubuntu虚拟机不显示ip地址【已解决】_ubuntu没有ip_不爱赖床的懒虫的博客-CSDN博客 sudo /sbin/dhclient VMTool安装与卸载 (201条消息) ubuntu中vmtools的安装与彻底卸载_卸载vmtools_林麦安的博客-CSDN博客 (202条消息) 解决虚拟机安装 VMware Tools 灰色无法…

聊聊RocketMQ中的broker的TPS和QPS为何相差巨大,是如何统计的

这里是weihubeats,觉得文章不错可以关注公众号小奏技术&#xff0c;文章首发。拒绝营销号&#xff0c;拒绝标题党 最近在看RocketMQ的一些监控指标的时候&#xff0c;总觉得一些监控指标不太对&#xff0c;好像对不上。 所以打算研究下看看RocketMQ中的 broker TPS、broker QP…

嵌入式学习笔记(60)内存管理之堆

1.7.1.什么是堆&#xff08;heap&#xff09; 内存管理对OS来说是一件非常复杂的事&#xff0c;因为首先内存容量大&#xff0c;其次内存需求在时间和大小块上没有规律&#xff08;OS上运行着几十、几百、几千个进程随时都会申请或者释放内存&#xff0c;申请或者释放的内存块…

JavaWeb从入门到起飞笔记——导学课程

学完这一节&#xff0c;我不知道学Web开发究竟能干什么&#xff1f;你知道吗&#xff1f; 以下是黑马程序员Java从入门到起飞的笔记 一、学完Javaweb能干什么&#xff1f; 学完Java后我们可以独立开发一些后台管理系统&#xff0c;例如CRMER器&#xff0c;京东和淘宝&#x…

tuxera ntfs2024破解版mac电脑磁盘读写软件

大家都知道由于操作系统的原因&#xff0c;在苹果电脑上不能够读写NTFS磁盘&#xff0c;但是&#xff0c;今天小编带来的这款tuxera ntfs 2024 mac版&#xff0c;完美的解决了这个问题。这是一款在macOS平台上使用的磁盘读写软件&#xff0c;能够实现苹果Mac OS X系统读写Micro…

C++ 字符串编码转换封装函数,UTF-8编码与本地编码互转

简介 字符串编码转换封装函数&#xff0c;UTF-8编码与本地编码互转。 中文乱码的解决方法 有时候我们会遇到乱码的字符串&#xff0c;比如&#xff1a; 古文码 可能是用GBK方式读取UTF-8编码的中文导致的&#xff0c;用下面的Utf8ToLocal(string str)函数转换一下就可以了。…

RFID解决光伏难题

RFID解决光伏难题 RFID是无线射频识别&#xff08;Radio Frequency Identification&#xff09;的缩写&#xff0c;是一种通过无线电信号识别物体的技术。RFID系统由标签、读写器和主机系统组成。标签内置有微型芯片和天线&#xff0c;能够储存和传输数据。读写器通过射频信号…

EasyCVR视频智能分析系统如何助力广场流动摊贩监管手段升级

在很多公园广场地带&#xff0c;经常会有流动摊贩进行售卖&#xff0c;虽然国家大力支持“地摊经济”&#xff0c;但很多摊贩并未按照规定进行摆摊&#xff0c;甚至有一些摊贩为了位置、客源大打出手&#xff0c;大大增添了城市管理难度。为让广场摊贩更加规范地进行作业&#…

mybatisplus开启sql打印的三种方式

1、在application.yml文件中添加mybatisplus的配置文件 使用mybatisplus自带的log-impl配置&#xff0c;可以在控制台打印出sql语句、执行结果的数据集、数据结果条数等详细信息&#xff0c;这种方法适合再调试的时候使用&#xff0c;因为这个展示的信息详细&#xff0c;更便于…

Windows11系统安装WSL教程

WSL&#xff0c;全称Windows Subsystem for Linux&#xff0c;是微软官方提供的可以在Windows上直接运行的Linux环境&#xff0c;包括大多数命令行工具、程序和应用&#xff0c;由系统底层虚拟机平台支持。 开启相关服务 1、控制面板-启用或关闭Windows功能 2、勾选以下两个…

Dataspell快捷键更改为eclipse后,在.py文件中shift+回车自动换行冲突问题解决

1.问题描述 已经将Dataspell快捷键切换快捷键为eclipse&#xff0c;在.py文件中shiftenter没有自动换行&#xff0c;出现如下结果。 2.问题解决 进去keymap配置界面 搜索python 将如下快捷键删除。 测试后问题已经解决。

Yakit工具篇:中间人攻击(平替Burp)的相关技巧-01

简介(来自官方文档) 背景 “MITM” 是 “Man-in-the-Middle” 的缩写&#xff0c;意思是中间人攻击。 MITM攻击是一种网络攻击技术&#xff0c;攻击者通过欺骗的手段&#xff0c;让自己成为通信双方之间的中间人&#xff0c;从而可以窃取双方之间的通信内容、修改通信内容、…

ntfs读写工具Tuxera NTFS2023激活码

Tuxera NTFS for Mac是一款mac系统读写工具,Tuxera让Mac OS支持NTFS 格式文件读写,支持所有移动硬盘、U盘等外接设备,同时Tuxera用户可以简单直观的在Mac机上随意对NTFS文件修改、 在 Mac 上打开、编辑、复制、移动或删除存储在 Windows NTFS 格式 USB 驱动器上的文件。当您获…

关于硬件原理图

很多纯软工程师开始做嵌入式时觉得门槛很高&#xff0c;基本的硬件原理图也看不懂&#xff0c;没有学过电路、数电、模电、电路原理&#xff0c;对电子元器件&#xff0c;对电阻、电压、电流、电容、电感等没有很扎实的概念&#xff0c;觉得老虎吃天无从下嘴。打开硬件原理图&a…

在Qt中怎么操作MySQL数据库

一、安装驱动 &#xff08;1&#xff09;安装 在Qt中操作MySQL数据库首先要安装mysql的驱动文件&#xff0c;将MySQL下的libmusql.dll文件复制到Qt的安装路径下的bin文件夹下即可。 本文福利&#xff0c;莬费领取Qt开发学习资料包、技术视频&#xff0c;内容包括&#xff08;…

使用Chrome浏览器进行网页截图

在需要截图的网页上&#xff0c;按F12打开开发调试页面&#xff0c;再按下ShiftCtrlP&#xff0c;打开命令输入框&#xff0c;输入Capture&#xff0c; 此时会弹出4中截图模式&#xff0c;我个人比较喜欢用Capture full size screenshot Capture area screenshot&#xff0c;…

YB6502是一款5V输入支持两节串联磷酸铁锂电池的升压充电管理应用

5V输入、双节串联磷酸铁锂电池升压充电芯片 描述&#xff1a; YB6502是一款5V输入支持两节串联磷酸铁锂电池的升压充电管理应用。采用了开 关升压结构&#xff0c;带有散热片的S0P8封装与较少的外部元件数日使得YB6502成为便携式 应用的理想选择。YB6502输入电压为5V,内置自适应…

Hadoop3教程(二十九):(生产调优篇)集群扩容及缩容(白名单与黑名单)

文章目录 &#xff08;150&#xff09;添加白名单&#xff08;151&#xff09;服役新服务器&#xff08;152&#xff09;服务器间数据均衡&#xff08;153&#xff09;黑名单退役服务器参考文献 这一章还算是比较重要的。 &#xff08;150&#xff09;添加白名单 白名单&#…