SpringSecurity(五)

news2024/11/26 9:43:28

深入理解HttpSecurity的设计

一、HttpSecurity的应用

  在前章节的介绍中我们讲解了基于配置文件的使用方式,也就是如下的使用。

image.png

  也就是在配置文件中通过 security:http 等标签来定义了认证需要的相关信息,但是在SpringBoot项目中,我们慢慢脱离了xml配置文件的方式,在SpringSecurity中提供了HttpSecurity等工具类,这里HttpSecurity就等同于我们在配置文件中定义的http标签。要使用的话方式如下。

image.png

  通过代码结果来看和配置文件的效果是一样的。基于配置文件的方式我们之前分析过,是通过标签对应的handler来解析处理的,那么HttpSecurity这块是如何处理的呢?我们来详细分析下。

二、HttpSecurity的类图结构

image.png

  可以看出HttpSecurity的类图结构相对比较简单,继承了一个父类,实现了两个接口。我们分别来看看他们的作用是什么?

1.SecurityBuilder接口

  我们先来看看SecurityBuilder接口,通过字面含义我们就可以发现这是一个帮我们创建对象的工具类。

public interface SecurityBuilder<O> {

	/**
	 * Builds the object and returns it or null.
	 * @return the Object to be built or null if the implementation allows it.
	 * @throws Exception if an error occurred when building the Object
	 */
	O build() throws Exception;

}

  通过源码我们可以看到在SecurityBuilder中给我们提供了一个build()方法。在接口名称处声明了一个泛型,而build()方法返回的正好是这个泛型的对象,其实就很好理解了,也就是SecurityBuilder会创建指定类型的对象。结合HttpSecurity中实现SecurityBuilder接口时指定的泛型我们可以看出创建的具体对象是什么类型。

image.png

  可以看出SecurityBuilder会通过build方法给我们创建一个DefaultSecurityFilterChain对象。也就是拦截请求的那个默认的过滤器链对象。

image.png

然后进入到doBuild()方法,会进入到AbstractConfiguredSecurityBuilder中的方法

	@Override
	protected final O doBuild() throws Exception {
		synchronized (this.configurers) {
			this.buildState = BuildState.INITIALIZING;
			beforeInit();
			init();
			this.buildState = BuildState.CONFIGURING;
			beforeConfigure();
			configure();
			this.buildState = BuildState.BUILDING;
	 		// 获取构建的对象,上面的方法可以先忽略
			O result = performBuild();
			this.buildState = BuildState.BUILT;
			return result;
		}
	}

进入到HttpSecurity中可以查看performBuild()方法的具体实现。

	@Override
	protected DefaultSecurityFilterChain performBuild() {
        // 对所有的过滤器做排序
		this.filters.sort(OrderComparator.INSTANCE);
		List<Filter> sortedFilters = new ArrayList<>(this.filters.size());
		for (Filter filter : this.filters) {
			sortedFilters.add(((OrderedFilter) filter).filter);
		}
		// 然后生成 DefaultSecurityFilterChain
		return new DefaultSecurityFilterChain(this.requestMatcher, sortedFilters);
	}

在构造方法中绑定了对应的请求匹配器和过滤器集合。

image.png

对应的请求匹配器则是 AnyRequestMatcher 匹配所有的请求。当然我们会比较关心默认的过滤器链中的过滤器是哪来的,这块儿我们继续来分析。

2.AbstractConfiguredSecurityBuilder

  然后我们再来看看AbstractConfiguredSecurityBuilder这个抽象类,他其实是SecurityBuilder的实现,在这儿需要搞清楚他们的关系。

image.png

类型作用
SecurityBuilder声明了build方法
AbstractSecurityBuilder提供了获取对象的方法以及控制一个对象只能build一次
AbstractConfiguredSecurityBuilder除了提供对对象细粒度的控制外还扩展了对configurer的操作

然后对应的三个实现类。

image.png

首先 AbstractConfiguredSecurityBuilder 中定义了一个枚举类,将整个构建过程分为 5 种状态,也可
以理解为构建过程生命周期的五个阶段,如下:

private enum BuildState {

		/**
		 * 还没开始构建
		 */
		UNBUILT(0),

		/**
		 * 构建中
		 */
		INITIALIZING(1),

		/**
		 * 配置中
		 */
		CONFIGURING(2),

		/**
		 * 构建中
		 */
		BUILDING(3),

		/**
		 * 构建完成
		 */
		BUILT(4);

		private final int order;

		BuildState(int order) {
			this.order = order;
		}

		public boolean isInitializing() {
			return INITIALIZING.order == this.order;
		}

		/**
		 * Determines if the state is CONFIGURING or later
		 * @return
		 */
		public boolean isConfigured() {
			return this.order >= CONFIGURING.order;
		}

	}

通过这些状态来管理需要构建的对象的不同阶段。

2.1 add方法

  AbstractConfiguredSecurityBuilder中方法概览

image.png

  我们先来看看add方法。

private <C extends SecurityConfigurer<O, B>> void add(C configurer) {
		Assert.notNull(configurer, "configurer cannot be null");
		Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
				.getClass();
		synchronized (this.configurers) {
			if (this.buildState.isConfigured()) {
				throw new IllegalStateException("Cannot apply " + configurer + " to already built object");
			}
			List<SecurityConfigurer<O, B>> configs = null;
			if (this.allowConfigurersOfSameType) {
				configs = this.configurers.get(clazz);
			}
			configs = (configs != null) ? configs : new ArrayList<>(1);
			configs.add(configurer);
			this.configurers.put(clazz, configs);
			if (this.buildState.isInitializing()) {
				this.configurersAddedInInitializing.add(configurer);
			}
		}
	}

	/**
	 * Gets all the {@link SecurityConfigurer} instances by its class name or an empty
	 * List if not found. Note that object hierarchies are not considered.
	 * @param clazz the {@link SecurityConfigurer} class to look for
	 * @return a list of {@link SecurityConfigurer}s for further customization
	 */
	@SuppressWarnings("unchecked")
	public <C extends SecurityConfigurer<O, B>> List<C> getConfigurers(Class<C> clazz) {
		List<C> configs = (List<C>) this.configurers.get(clazz);
		if (configs == null) {
			return new ArrayList<>();
		}
		return new ArrayList<>(configs);
	}

  add 方法,这相当于是在收集所有的配置类。将所有的 xxxConfigure 收集起来存储到 configurers
中,将来再统一初始化并配置,configurers 本身是一个 LinkedHashMap ,key 是配置类的 class,
value 是一个集合,集合里边放着 xxxConfigure 配置类。当需要对这些配置类进行集中配置的时候,
会通过 getConfigurers 方法获取配置类,这个获取过程就是把 LinkedHashMap 中的 value 拿出来,
放到一个集合中返回。

2.2 doBuild方法

  然后来看看doBuild方法中的代码

 	@Override
	protected final O doBuild() throws Exception {
		synchronized (this.configurers) {
			this.buildState = BuildState.INITIALIZING;
			beforeInit(); //是一个预留方法,没有任何实现
			init(); // 就是找到所有的 xxxConfigure,挨个调用其 init 方法进行初始化
			this.buildState = BuildState.CONFIGURING;
			beforeConfigure(); // 是一个预留方法,没有任何实现
			configure(); // 就是找到所有的 xxxConfigure,挨个调用其 configure 方法进行配置。
			this.buildState = BuildState.BUILDING;
			O result = performBuild();
// 是真正的过滤器链构建方法,但是在 AbstractConfiguredSecurityBuilder中 performBuild 方法只是一个抽象方法,具体的实现在 HttpSecurity 中
			this.buildState = BuildState.BUILT;
			return result;
		}
	}

init方法:完成所有相关过滤器的初始化

	private void init() throws Exception {
		Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
		for (SecurityConfigurer<O, B> configurer : configurers) {
			configurer.init((B) this); // 初始化对应的过滤器
		}
		for (SecurityConfigurer<O, B> configurer : this.configurersAddedInInitializing) {
			configurer.init((B) this);
		}
	}

configure方法:完成HttpSecurity和对应的过滤器的绑定。

	private void configure() throws Exception {
		Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
		for (SecurityConfigurer<O, B> configurer : configurers) {
			configurer.configure((B) this);
		}
	}

3.HttpSecurity

  HttpSecurity 做的事情,就是进行各种各样的 xxxConfigurer 配置

image.png

HttpSecurity 中有大量类似的方法,过滤器链中的过滤器就是这样一个一个配置的。我们就不一一介绍
了。每个配置方法的结尾都会来一句 getOrApply,这个是干嘛的?我们来看下

	private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply(C configurer)
			throws Exception {
		C existingConfig = (C) getConfigurer(configurer.getClass());
		if (existingConfig != null) {
			return existingConfig;
		}
		return apply(configurer);
	}

  getConfigurer 方法是在它的父类 AbstractConfiguredSecurityBuilder 中定义的,目的就是去查看当前
这个 xxxConfigurer 是否已经配置过了。
  如果当前 xxxConfigurer 已经配置过了,则直接返回,否则调用 apply 方法,这个 apply 方法最终会调
用到 AbstractConfiguredSecurityBuilder#add 方法,将当前配置 configurer 收集起来
HttpSecurity 中还有一个 addFilter 方法.

	@Override
	public HttpSecurity addFilter(Filter filter) {
		Integer order = this.filterOrders.getOrder(filter.getClass());
		if (order == null) {
			throw new IllegalArgumentException("The Filter class " + filter.getClass().getName()
					+ " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead.");
		}
		this.filters.add(new OrderedFilter(filter, order));
		return this;
	}

  这个 addFilter 方法的作用,主要是在各个 xxxConfigurer 进行配置的时候,会调用到这个方法,
(xxxConfigurer 就是用来配置过滤器的),把 Filter 都添加到 fitlers 变量中。

小结:这就是 HttpSecurity 的一个大致工作流程。把握住了这个工作流程,剩下的就只是一些简单的重
复的 xxxConfigurer 配置了

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

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

相关文章

自媒体原创文章写作技巧,轻松过原创首发

在写作的世界里&#xff0c;原创文章是一种价值的象征&#xff0c;但对于新手小白来说&#xff0c;可能会觉得写出真正原创的内容颇具挑战。然而&#xff0c;实现原创并不是一件遥不可及的事情。在这篇文章中&#xff0c;我将分享10招&#xff0c;教你如何成为一位写作中的原创…

联想范建平:联想混合AI架构具备两大明显优势

12月7日&#xff0c;首届AI PC创新论坛在北京联想集团总部举办。联想集团副总裁、联想研究院人工智能实验室负责人范建平表示&#xff0c;为提供真正可信、个性化的AI专属服务&#xff0c;联想提出了混合智能&#xff08;Hybrid AI&#xff09;概念&#xff0c;并已经显现出更强…

SFX的妙用——如何在不安装软件的情况下打开自定义格式文件?

前段时间看到群友讨论压缩包能不能运行&#xff0c;想起了n年前用自解压文件SFX实现的一个“需求”&#xff1a;在没有安装任何应用软件的Windows&#xff08;当时还要支持XP&#xff09;上能双击打开自定义格式的文件。当时第一反应是这“需求”太奇葩了&#xff0c;简直是不可…

Pacifist:一款专为技术开发者打造的软件提取工具

对于技术开发者而言&#xff0c;有效且便捷的工具可以显著提高工作效率。Pacifist&#xff0c;作为一款专业的软件提取工具&#xff0c;专为技术开发者而设计&#xff0c;旨在提供简单、安全的软件提取和管理工作。 一、Pacifist的技术特点 Pacifist主要采用AppleScript作为其…

QT Windos平台下打包应用程序

一、windeployqt.exe windeployqt&#xff1a;是 Qt 框架自带的一个工具&#xff0c;用于将一个 Qt 应用程序在 Windows 操作系统下进行打包。它可以通过扫描应用程序的依赖项获取所需的 Qt 库文件、插件和翻译文件&#xff0c;以及复制应用程序可执行文件和所需的依赖项到指定…

挑选分支中某一个提交进行合并

复制提交的哈希(sha-1)值 挑选提交 git cherry-pick 复制过来的哈希值 若有冲突&#xff0c;解决冲突&#xff0c;没有冲突&#xff0c;即合并完成

Java---线程讲解(二)

文章目录 1. Runnable接口2. 卖票案例3. 同步代码块解决数据安全问题4. 同步方法解决数据安全问题5. 线程安全的类6. Lock锁 1. Runnable接口 1. 创建线程的另一种方法是声明一个实现Runnable接口的类&#xff0c;之后重写run()方法&#xff0c;然后可以分配类的实例&#xff0…

数据结构与算法编程题50

假设不带权有向图采用邻接矩阵G存储&#xff0c;设计实现以下功能的算法。 &#xff08;1&#xff09;求出图中每个顶点的出度。 &#xff08;2&#xff09;求出图中出度为0的顶点数。 &#xff08;3&#xff09;求出图中每个顶点的入度。 //参考博客&#xff1a;https://blog.…

数据库数据恢复—sqlserver数据库文件被加密,文件名被篡改的数据恢复案例

SQLServer数据库故障&#xff1a; 某公司服务器上的SQLServer数据库被加密&#xff0c;无法使用。被加密的数据库有2个&#xff0c;数据库的MDF、LDF、log文件名字被篡改。 数据库被加密截图&#xff1a; 数据库备份被加密&#xff0c;文件名字被篡改&#xff1a; SQLServer数…

Navicat 连接 GaussDB分布式的快速入门

Navicat Premium&#xff08;16.3.3 Windows版或以上&#xff09;正式支持 GaussDB 分布式数据库。GaussDB分布式模式更适合对系统可用性和数据处理能力要求较高的场景。Navicat 工具不仅提供可视化数据查看和编辑功能&#xff0c;还提供强大的高阶功能&#xff08;如模型、结构…

STM32CubeMx+MATLAB Simulink串口输出实验

STM32CubeMxMATLAB Simulink串口输出实验 &#x1f4cc;《STM32CubeMxMATLAB Simulink点灯程序》&#x1f4cd;相关篇《MATLAB Simulink STM32硬件在环 &#xff08;HIL&#xff09;实现例程测试》&#x1f516;需要的软件支持包&#xff1a;Embedded Coder Support Package fo…

【Java 基础】21 多线程同步与锁

文章目录 1.存在的问题2.使用同步解决问题1) synchronized2) volatile3) 锁 总结 用多线程过程中&#xff0c;有可能出现 多个线程同时处理&#xff08;获取或修改等&#xff09;同一个数据&#xff0c;这个时候就 会发生数据不同步的问题&#xff0c; 因此出现了同步和锁来…

html和css写淘宝的快速导航条

目录 1、css代码 2、html代码 1、css代码 <style>* {margin: 0;padding: 0;list-style: none;text-decoration: none;}.nav {width: 900px;height: 40px;background-color: rgb(247, 249, 250);margin: 50px auto;padding-left: 30px;}.nav>li {float: left;width: 1…

软件测试中年危机?30岁大关?“我“该如何破局...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 在软件测试行业摸…

Oracle 表数据锁了,处理方式,Oracle锁表处理

Oracle 表数据锁了&#xff0c;处理方式&#xff0c;Oracle锁表处理 参考连接&#xff1a; Oracle锁表处理_oracle锁表怎么解决_辣椒炒鸡的博客-CSDN博客 实践&#xff1a; 1、查看被锁的表 select b.owner,b.object_name,a.session_id,a.locked_mode from v$locked_object …

QTimer的使用

参考&#xff1a; Qt 定时器 (QTimer)的几种使用方法-CSDN博客 QTimer 如何判断QT定时器在工作&#xff1f;-CSDN博客 (1)timeout信号 每隔5秒&#xff0c;timer发射timeout()信号&#xff0c;w执行onTimeout()槽函数。 Widget w;QTimer timer;timer.start(5*1000);QObjec…

点评项目——商户查询缓存

2023.12.7 redis实现商户查询缓存 在企业开发中&#xff0c;用户的访问量动辄成百上千万&#xff0c;如果没有缓存机制&#xff0c;数据库将承受很大的压力。本章我们使用redis来实现商户查询缓存。 原来的操作是根据商铺id直接从数据库查询商铺信息&#xff0c;为了防止频繁地…

WIFI7:O7851PM吞吐测试

近年来&#xff0c;随着科技的飞速发展&#xff0c;WIFI技术也在不断演进&#xff0c;从WIFI6逐渐过渡到WIFI6E&#xff0c;再到如今备受关注的WIFI7。这次我们将聚焦于我们欧飞信最新研发的WIFI7模组&#xff1a;O7851PM&#xff0c;通过对WIFI7的核心指标——吞吐量测试&…

IoT DC3 是一个基于 Spring Cloud 全开源物联网平台 linux docker部署傻瓜化步骤

如有不了解可先参考我的另一篇文章本地部署:IoT DC3 是一个基于 Spring Cloud 的开源的、分布式的物联网(IoT)平台本地部署步骤 如有不了解可先参考我的另一篇文章本地部署: 1 环境准备: JDK 8 以上 docker 安装好 下载docker-compose-dev.yml 文件 执行基础环境docker安装 …

如何使用技术 SEO 优化 Pinterest 富图钉

Pinterest 可以影响搜索引擎排名&#xff0c;尤其是谷歌。不过&#xff0c;它的作用方式与其他搜索引擎优化因素不同。这就是 Google 将图钉放在 nofollow 列表中。但是&#xff0c;它们仍然可以作为搜索引擎优化的一个重要因素。 高质量的图钉具有高分辨率的图片、吸引人的内…