当前位置: 首页 > article >正文

Spring Security 6 系列之三 - Filter过滤器

之所以想写这一系列,是因为之前工作过程中使用Spring Security,但当时基于spring-boot 2.3.x,其默认的Spring Security是5.3.x。之后新项目升级到了spring-boot 3.3.0,结果一看Spring Security也升级为6.3.0,关键是其风格和内部一些关键Filter大改,导致在配置同样功能时,多费了些手脚,因此花费了些时间研究新版本的底层原理,这里将一些学习经验分享给大家。

注意由于框架不同版本改造会有些使用的不同,因此本次系列中使用基本框架是 spring-boo-3.3.0(默认引入的Spring Security是6.3.0),JDK版本使用的是19,所有代码都在spring-security-study项目上:https://github.com/forever1986/spring-security-study.git

目录

  • 1 过滤器原理
  • 2 Spring Security的过滤器链
  • 3 重要的过滤器
    • 3.1 SecurityContextHolderFilter(SecurityContextConfigurer)
      • 3.1.1 SecurityContextRepository:获取登陆过的SecurityContext
      • 3.1.2 SecurityContextHolderStrategy:存储本次请求SecurityContext,供后续Filter使用
    • 3.2 UsernamePasswordAuthenticationFilter(FormLoginConfigurer)
    • 3.3 BasicAuthenticationFilter(HttpBasicConfigurer)
    • 3.4 DefaultLoginPageGeneratingFilter(DefaultLoginPageConfigurer)
    • 3.5 LogoutFilter(LogoutConfigurer)
    • 3.6 DefaultLogoutPageGeneratingFilter(DefaultLoginPageConfigurer)
    • 3.7 ExceptionTranslationFilter(ExceptionHandlingConfigurer)
    • 3.8 AuthorizationFilter(AuthorizeHttpRequestsConfigurer)

前面介绍了Spring Security的基本入门以及如何自定义用户,但关于Spring Security的基本实现原理还没有好好讲一下。本章就带你了解一下Spring Security的底层原理。

1 过滤器原理

之所以先讲过滤器,是因为Spring Security底层依赖Servlet的过滤器技术。提到过滤器之前,先要理解一个web请求,到达服务器的时候,其处理的方式是通过Servlet。我还记得刚开始学习java web的时候(前后端还没有分离的时候),就写过Servlet,其实就是处理web请求返回资源。包括后来的JSP也是将其转换为一个Servlet,JSP只不过是为了适应展现页面的编程方式。
而过滤器(Filter)是 Servlet 规范中的一部分,它可以在请求到达 Servlet 之前对请求进行预处理,也可以在响应返回给客户端之前对响应进行后处理。过滤器可以用于实现诸如日志记录、字符编码转换、安全控制等功能。比如常见服务器Tomcat的过滤器配置就是可以在 web.xml 文件中进行配置的。

在这里插入图片描述
Filter接口定义了3个方法:doFilter,init和destory,其中doFilter就是请求进入过滤器时需要执行的逻辑。

2 Spring Security的过滤器链

了解完过滤器之后,我们再来看看Spring Security是如何通过过滤器实现自己的认证和授权的。

  • 首先,我们在使用springboot的web其实也是Tomcat、jetty等web服务器,这些web服务器基于本身Servlet 容器中的过滤器是由 Servlet 容器直接管理的,这样就使得你不能方便地使用 Spring 的依赖注入来管理过滤器的依赖,因此Spring提供DelegatingFilterProxy,简单理解就是Spring通过DelegatingFilterProxy可以将你在Spring中注册的过滤器或者过滤器链加入到Servlet 过滤器中。
  • 其次,Spring Security的过滤器是通过FilterChainProxy代理方式加入到链路中,FilterChainProxy 是 Spring Security 提供的一个特殊过滤器,它允许通过 SecurityFilterChain 向多个过滤器实例委托,它通常被封装在 DelegatingFilterProxy中。
  • 最后,Spring Security的SecurityFilterChain才是最终过滤器所在。整体流程如下图

注意:Spring Security是很多个组成一条链路,而且这些过滤器是有顺序。知道这一点对后续自定义功能就比较容易理解。

在这里插入图片描述

3 重要的过滤器

那么在Spring Security中有二、三十个Filter,默认配置下就有16个过滤器(如下图),其实很多你可能不需要关心。但是下面这些Filter你需要了解其原理,因为后续自定义配置上面需要了解其不同Filter的功能,才能自定义配置。
在这里插入图片描述

重要提示:过滤器源码,一般先看doFilter方法,这个方法就是实现其作用的。另外如果觉得看起来有些迷糊,可以先看后面几个自定义配置的章节(自定义登录界面、异常处理、前后端分离等),里面都会讲解与其相关的过滤器。这时候再回来看看这部分会更加清楚

注意:对于Spring Security的每个Filter过滤器的配置都会有对应的AbstractHttpConfigurer,该类是用来配置Filter,Configurer主要看其configure(H http)方法,想知道如何配置或有什么配置,可以直接看源码相关的Configurer类就能知道。

3.1 SecurityContextHolderFilter(SecurityContextConfigurer)

SecurityContextHolderFilter对应的配置类是SecurityContextConfigurer,由SecurityContextConfigurer加入过滤器链的。

SecurityContextHolderFilter:是一个用来存储和获取当前请求的上下文SecurityContext。这个做过后端的朋友很好理解,比如我们前端访问后端会传入用户信息,为了能够在后端处理过程中随时获得用户信息,我们会通过一个拦截器存储用户信息到一个ThreadLocal对象中,这样就不用作为参数一直传递下去,而是随时通过ThreadLocal对象获取,关于使用拦截器+ThreadLocal存储临时存储用户信息可以参考我写过的java脚手架系列中的拦截器文章。

SecurityContextHolderFilter,如下图先看doFilter方法,其中有2个变量很重要,分别是SecurityContextRepositorySecurityContextHolderStrategy
在这里插入图片描述

原理:SecurityContextHolderFilter中通过SecurityContextRepository获取已经登陆过用户信息SecurityContext,再通过SecurityContextHolderStrategy将本次SecurityContext存储起来,供后面过滤器使用。

3.1.1 SecurityContextRepository:获取登陆过的SecurityContext

SecurityContextHolderFilter原理中我们知道Spring Security会从SecurityContextRepository获取用户信息(如果登陆过,则有用户信息,否则就会要求去登录)。SecurityContextRepository也有4种实现方式(当然你可以自己扩展),它们分别是

  • HttpSessionSecurityContextRepository:它在请求之间的HttpSession中存储安全上下文。将查询HttpSession以在loadContext方法中检索SecurityContext(默认使用SPRING_SECURITY_CONTEXT_KEY)。简单来说就是采用web服务器的session存储。
  • NullSecurityContextRepository:不做任何处理,意味着获取都是空的SecurityContext
  • RequestAttributeSecurityContextRepository:将SecurityContext存储在一个jakarta上。servlet。ServletRequest。setAttribute(String, Object),以便在发生不同的分派类型时可以恢复它。在随后的请求中将不可用。意思就是一次性,下次访问则不找不到了。
  • DelegatingSecurityContextRepository:代理类,可以是以上三种的其中一种或者自定义的。

在这里插入图片描述

Spring Security 默认使用HttpSessionSecurityContextRepository方式

3.1.2 SecurityContextHolderStrategy:存储本次请求SecurityContext,供后续Filter使用

SecurityContextHolderStrategy是设置上下文SecurityContext在整个链路传递的存储方式,它有4个不同的实现类或者说4中不同的存储策略(当然你可以自己扩展),它们分别是:

  • GlobalSecurityContextHolderStrategy:基于全局,把SecurityContext存储为static变量
  • ThreadLocalSecurityContextHolderStrategy:基于ThreadLocal,把SecurityContext存入ThreadLocal变量
  • InheritableThreadLocalSecurityContextHolderStrategy:基于InheritableThreadLocal,关于InheritableThreadLocal和ThreadLocal区别,可以参考我写过的一篇《ThreadLocal引发的思考》
  • ListeningSecurityContextHolderStrategy:基于监听机制

在这里插入图片描述

Spring Security 默认使用ThreadLocalSecurityContextHolderStrategy方式

重要提示:SecurityContextHolderFilter比较重要,比如在Session共享、JWT都会讲到这个过滤器。

3.2 UsernamePasswordAuthenticationFilter(FormLoginConfigurer)

UsernamePasswordAuthenticationFilter对应的配置类是FormLoginConfigurer,由FormLoginConfigurer加入过滤器链的。

UsernamePasswordAuthenticationFilter:是一个做用户名密码认证的过滤器。我们之前说过Spring Security有多种认证方式,通过表单的用户名密码认证,就是通过这个过滤器来实现的。这个在系列二中已经详细讲过该过滤器的认证原理,这里就不再重复讲。但是需要补充一点,就是里面有几个常量,其实就是定义获取请求中的username和password,如果自定义表单,也需要按照这2个参数来。“/login”就是拦截的请求,当然你也可以修改,这个在后面自定义配置再说。
在这里插入图片描述

3.3 BasicAuthenticationFilter(HttpBasicConfigurer)

BasicAuthenticationFilter对应的配置类是HttpBasicConfigurer,由HttpBasicConfigurer加入过滤器链的。

BasicAuthenticationFilter:是一个基于HTTP Basic authentication的认证方式,如下图先看doFilter方法,与上面UsernamePasswordAuthenticationFilter基本认证流程一样,唯一不同之处在于获取用户名和密码不同,是通过AuthenticationConverter获取的。
在这里插入图片描述
我们再看看AuthenticationConverter是new了一个BasicAuthenticationConverter
在这里插入图片描述
我们再看看BasicAuthenticationConverter的convert方法,就是取得头部的Authorization。
在这里插入图片描述

3.4 DefaultLoginPageGeneratingFilter(DefaultLoginPageConfigurer)

DefaultLoginPageGeneratingFilter对应的配置类是DefaultLoginPageConfigurer,由DefaultLoginPageConfigurer加入过滤器链的。

DefaultLoginPageGeneratingFilter:是一个生成默认的登录页面的过滤器,如果你不是自定义的登录页面,那么Spring Security会自动给你生成一个登录页面。如下图先看器doFilter方法,2个地方比较重要,一个是判断什么情况返回登录界面,一个是生成登录界面
在这里插入图片描述

3.5 LogoutFilter(LogoutConfigurer)

LogoutFilter对应的配置类是LogoutConfigurer,由LogoutConfigurer加入过滤器链的。

LogoutFilter:处理”/logout”请求。如下图先看doFilter方法,通过判断请求是否是登出,如果是登出,做2件事情:

  • 调用handler清除登录信息SecurityContext,这个handler有很多实现类,其初始化时一个CompositeLogoutHandler,可以包括多个handler
  • 并调用SuccessHandler返回登出结果。默认是SimpleUrlLogoutSuccessHandler看其源码是重定向URL,其实是跳到登录页面

在这里插入图片描述> 提示:如果前后端分离,那么在做登出的时候,一般是返回给前端一个json数据,这时候你就可以重写SuccessHandler

3.6 DefaultLogoutPageGeneratingFilter(DefaultLoginPageConfigurer)

DefaultLogoutPageGeneratingFilter对应的配置类是DefaultLoginPageConfigurer,由DefaultLoginPageConfigurer加入过滤器链的。

DefaultLogoutPageGeneratingFilter:生成默认登出确认页面。如下图先看doFilter方法,判断是否符合登出请求URL,然后生成一个确认登出页面
在这里插入图片描述

3.7 ExceptionTranslationFilter(ExceptionHandlingConfigurer)

ExceptionTranslationFilter对应的配置类是ExceptionHandlingConfigurer,由ExceptionHandlingConfigurer加入过滤器链的。

ExceptionTranslationFilter:主要用来处理Spring Security链路过程中抛出的认证异常和授权异常。如下图先看doFilter方法,它并没有处理所有异常,除了认证异常(AuthenticationException)和授权异常(AccessDeniedException)之外,其它全部被抛出。
在这里插入图片描述

提示:对于上图最后一个调用handleSpringSecurityException方法,有兴趣可以下钻看Spring Security如何处理认证和授权异常,其实是分别调用AuthenticationEntryPoint和AccessDeniedHandler,也就是说如果我们想自定义异常返回方式,就可以重写这2个类。关于这部分,后续的异常处理章节会详细讲解。

3.8 AuthorizationFilter(AuthorizeHttpRequestsConfigurer)

AuthorizationFilter对应的配置类是AuthorizeHttpRequestsConfigurer,由AuthorizeHttpRequestsConfigurer加入过滤器链的。

AuthorizationFilter:作为最后一个用于授权的过滤,判断是否有权限访问资源。如下图先看doFilter方法,先通过authorizationManager获得判断权限结果,在看看AuthorizationDecision里面的granted是否为true。
在这里插入图片描述

关于AuthorizationFilter,后续在授权章节,我们再详细讲解

结语:关于Spring Security的底层原理大概讲这么多。Spring Security的过滤器有那么多,我们这里主要讲的是几个经常使用到的,其它的过滤器后续遇到需要自定义配置的时候,我们再讲解。Spring Security默认配置显然不能符合一个真正的业务需求,那么下一章我们就要开始做各种自定义配置。


http://www.kler.cn/a/446366.html

相关文章:

  • 杂七杂八的网络安全知识
  • 两点间最短距离 - Dijkstra
  • python 曲线拟合,曲线拟合交点
  • 基于MATLAB的图像增强
  • 基于层次化设计方法,设计一个16位二进制全加器
  • 武汉市电子信息与通信工程职称公示了
  • Xcode 16 编译弹窗问题、编译通过无法,编译通过打包等问题汇总
  • 四大跨平台开发框架深度解析——uniapp、uniapp-X、React Native与Flutter
  • 服务器被入侵登录不上怎么办?
  • 0基础学前端-----CSS DAY9
  • 【JavaEE进阶】第一个Spring Boot程序
  • python elasticsearch 8.x通过代理发起请求方法
  • VMware安装Ubuntu24.04以及安装好后初步使用配置!
  • CSS系列(27)- 图形与滤镜详解
  • List深拷贝后,数据还是被串改
  • 监控易:开启摄像头故障监控的卓越之钥
  • 挑战一个月基本掌握C++(第七天)了解指针,引用,时间,输入输出,结构体,vector容器,数据结构 - 通用完结
  • go 聊天系统项目-5 客户端发消息
  • Kubernetes(k8s)离线部署DolphinScheduler3.2.2
  • C# 动态组合判断条件对数据进行筛选
  • 大厂 Java 架构师面试题全解析
  • 【人工智能数学基础篇】——深入详解矩阵与向量运算及矩阵分解技术,打牢人工智能知识基础
  • OpenHarmony-4.HDI 框架
  • Github 2024-12-21 Rust开源项目日报 Top10
  • react websocket 全局访问和响应
  • Flink CDC 生产环境常用参数总结