spring-security原理与应用系列:建造者

目录

1.构建过程

AbstractSecurityBuilder

AbstractConfiguredSecurityBuilder

WebSecurity

2.建造者类图

SecurityBuilder

​​​​​​​AbstractSecurityBuilder

​​​​​​​AbstractConfiguredSecurityBuilder

​​​​​​​WebSecurity

3.小结


        紧接上一篇文章,这一篇我们来看看构建者WebSecurity是如何构建出一个过滤器对象springSecurityFilterChain的。

1.构建过程

        点击类WebSecurityConfiguration的方法springSecurityFilterChain()里的this.webSecurity.build(),如下所示:

AbstractSecurityBuilder

public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {

... ...

public final O build() throws Exception {
   if (this.building.compareAndSet(false, true)) {
      this.object = doBuild();
      return this.object;
   }
   throw new AlreadyBuiltException("This object has already been built");
}

        在这里,build()方法调用了doBuild()方法。

        点击doBuild()方法,如下所示:

​​​​​​​AbstractConfiguredSecurityBuilder

@Override
protected final O doBuild() throws Exception {
   synchronized (configurers) {
      buildState = BuildState.INITIALIZING;
      beforeInit();
      init();
      buildState = BuildState.CONFIGURING;
      beforeConfigure();
      configure();
      buildState = BuildState.BUILDING;
      O result = performBuild();
      buildState = BuildState.BUILT;
      return result;
   }
}

        在这里,定义了构建对象的所有步骤。包括beforeInit、init、beforeConfigure、configure、performBuild的5个步骤。

        点击最后一步performBuild,如下所示:

​​​​​​​WebSecurity

public final class WebSecurity extends
      AbstractConfiguredSecurityBuilder<Filter, WebSecurity> implements
      SecurityBuilder<Filter>, ApplicationContextAware {

... ...

@Override
protected Filter performBuild() throws Exception {
   int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
   List<SecurityFilterChain> securityFilterChains = new ArrayList<SecurityFilterChain>(
         chainSize);
   for (RequestMatcher ignoredRequest : ignoredRequests) {
      securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
   }
   for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
      securityFilterChains.add(securityFilterChainBuilder.build());
   }
   FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
   if (httpFirewall != null) {
      filterChainProxy.setFirewall(httpFirewall);
   }
   filterChainProxy.afterPropertiesSet();
   Filter result = filterChainProxy;
   if (debugEnabled) {
      result = new DebugFilter(filterChainProxy);
   }
   postBuildAction.run();
   return result;
}

        在这里,可以看到构建的过滤器类是FilterChainProxy。至于这个类的具体结构,我们后续再进行深入的探究。

        我们先看看在系统运行时,这个FilterChainProxy内部都包含有哪些Filter。

        设置断点,如下所示:

​​​​​​​过滤器代理

        在这里,我们看到了FilterChainProxy对象内部包含了很多的Filter。后续再深入了解这些Filter的配置过程及应用场景。

        接下来我们重点学习一下与WebSecurity构建者相关的类图模型。

2.建造者类图

       在这里,AbstractSecurityBuilder、AbstractConfiguredSecurityBuilder、WebSecurity都是我们在上面一节中有接触过的类。

​​​​​​​SecurityBuilder

        这个接口是建造者模式的顶级接口,含有建造者对外暴露的构建对象的一个接口方法 build() 。

        代码如下:

public interface SecurityBuilder<O> {
   O build() throws Exception;
}

​​​​​​​AbstractSecurityBuilder

        这个类是SecurityBuilder接口的抽象子类,实现了接口的build()方法,为确保构建对象只被构建一次,对父接口方法 build() 进行了原子判断,从而保证每次只构建一次。另外,定义了一个抽象方法 doBuild() 供子类扩展。

        代码如下:

public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
   private AtomicBoolean building = new AtomicBoolean();
   private O object;
   public final O build() throws Exception {
      if (this.building.compareAndSet(false, true)) {
         this.object = doBuild();
         return this.object;
      }
      throw new AlreadyBuiltException("This object has already been built");
   }
   public final O getObject() {
      if (!this.building.get()) {
         throw new IllegalStateException("This object has not been built");
      }
      return this.object;
   }
   protected abstract O doBuild() throws Exception;
}

​​​​​​​AbstractConfiguredSecurityBuilder

        实现父类的doBuild()方法,这里是真正执行构建的地方。

        首先,使用了建造者模式定义了构建对象的所有步骤;

        其次,使用了模板方法模式,将构建对象的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。

        代码如下:

public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>extends AbstractSecurityBuilder<O> {... ...public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {add(configurer);return configurer;}@SuppressWarnings("unchecked")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 (configurers) {if (buildState.isConfigured()) {throw new IllegalStateException("Cannot apply " + configurer+ " to already built object");}List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers.get(clazz) : null;if (configs == null) {configs = new ArrayList<>(1);}configs.add(configurer);this.configurers.put(clazz, configs);if (buildState.isInitializing()) {this.configurersAddedInInitializing.add(configurer);}}}@Overrideprotected final O doBuild() throws Exception {synchronized (configurers) {buildState = BuildState.INITIALIZING;beforeInit();init();buildState = BuildState.CONFIGURING;beforeConfigure();configure();buildState = BuildState.BUILDING;O result = performBuild();buildState = BuildState.BUILT;return result;}}protected void beforeInit() throws Exception {}protected void beforeConfigure() throws Exception {}protected abstract O performBuild() throws Exception;@SuppressWarnings("unchecked")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 : configurersAddedInInitializing)      {configurer.init((B) this);}}@SuppressWarnings("unchecked")private void configure() throws Exception {Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();for (SecurityConfigurer<O, B> configurer : configurers) {configurer.configure((B) this);}}
}

​​​​​​​WebSecurity

        实现父类的 performBuild()方法,这是构建对象的所有步骤的最后一个步。通过实现父类方法的方式来定义具体的执行内容。

        WebSecurity 的目标是构建 FilterChainProxy 对象,即构建核心过滤器 springSecurityFilterChain。

        代码如下:

public final class WebSecurity extends
      AbstractConfiguredSecurityBuilder<Filter, WebSecurity> implements
      SecurityBuilder<Filter>, ApplicationContextAware {
      ... ...
   @Override
   protected Filter performBuild() throws Exception {
      int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
      List<SecurityFilterChain> securityFilterChains = new ArrayList<>(
            chainSize);
      for (RequestMatcher ignoredRequest : ignoredRequests) {
         securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
      }
      for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
         securityFilterChains.add(securityFilterChainBuilder.build());
      }
      FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
      if (httpFirewall != null) {
         filterChainProxy.setFirewall(httpFirewall);
      }
      filterChainProxy.afterPropertiesSet();

      Filter result = filterChainProxy;
      postBuildAction.run();
      return result;
   }
}

3.小结

        整个构建过程由beforeInit、init、beforeConfigure、configure、performBuild5大步骤组成。技术层面使用了建造者模式和模板方法模式。


疏漏之处恭请雅正,良策佳议敬候惠示,凡所赐教必当铭感于心。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

结合代码理解Spring AOP的概念(切面、切入点、连接点等)

前情回顾 对AOP的理解 我这篇文章介绍了为什么要有AOP&#xff08;AOP解决了什么问题&#xff09;以及如何实现AOP。但在实现AOP的时候&#xff0c;并未探讨AOP相关概念&#xff0c;例如&#xff1a;切面、切入点、连接点等。因此&#xff0c;本篇文章希望结合代码去理解Spring…

【AI大模型】搭建本地大模型GPT-NeoX:详细步骤及常见问题处理

搭建本地大模型GPT-NeoX:详细步骤及常见问题处理 GPT-NeoX是一个开源的大型语言模型框架,由EleutherAI开发,可用于训练和部署类似GPT-3的大型语言模型。本指南将详细介绍如何在本地环境中搭建GPT-NeoX,并解决过程中可能遇到的常见问题。 1. 系统要求 1.1 硬件要求 1.2 软…

Copilot提示词库用法:调整自己想要的,记住常用的,分享该共用的

不论你是 Microsoft 365 Copilot 的新用户还是熟练运用的老鸟&#xff0c;不论你是使用copilot chat&#xff0c;还是在office365中使用copilot&#xff0c;copilot提示词库都将帮助你充分使用copilot这一划时代的产品。它不仅可以帮助你记住日常工作中常用的prompt提示词&…

Spring:AOP

一、AOP概念的引入 为了更好地介绍AOP&#xff0c;我们以登录作为示例。 首先&#xff0c;我们先来看一下登录的原理&#xff1a; 如图所示&#xff0c;这是一个基本的登录原理图&#xff0c;但是如果我们想要在这个登录过程上再添加一些新的功能&#xff0c;比如权限校验&am…

Ubuntu实时读取音乐软件的音频流

文章目录 一. 前言二. 开发环境三. 具体操作四. 实际效果 一. 前言 起因是这样的&#xff0c;我需要在Ubuntu中&#xff0c;实时读取正在播放音乐的音频流&#xff0c;然后对音频进行相关的处理。本来打算使用的PipewireHelvum的方式实现&#xff0c;好处是可以直接利用Helvum…

CUDA 学习(4)——CUDA 编程模型

CPU 和 GPU 由于结构的不同&#xff0c;具有不同的特点&#xff1a; CPU&#xff1a;擅长流程控制和逻辑处理&#xff0c;不规则数据结构&#xff0c;不可预测存储结构&#xff0c;单线程程序&#xff0c;分支密集型算法GPU&#xff1a;擅长数据并行计算&#xff0c;规则数据结…

前端会话控制技术:cookie/session/token

目录 前端中的 Cookie、Session 和 Token&#xff1a;详解与应用1. Cookie1.1 什么是 Cookie&#xff1f;1.2 Cookie 的工作原理1.3 Cookie 的特点1.4 Cookie 的用途1.5 Cookie 的安全性 2. Session2.1 什么是 Session&#xff1f;2.2 Session 的工作原理2.3 Session 的特点2.4…

MATLAB实现基于“蚁群算法”的AMR路径规划

目录 1 问题描述 2 算法理论 3 求解步骤 4 运行结果 5 代码部分 1 问题描述 移动机器人路径规划是机器人学的一个重要研究领域。它要求机器人依据某个或某些优化原则 (如最小能量消耗&#xff0c;最短行走路线&#xff0c;最短行走时间等)&#xff0c;在其工作空间中找到一…

Shopify Checkout UI Extensions

结账界面的UI扩展允许应用开发者构建自定义功能&#xff0c;商家可以在结账流程的定义点安装&#xff0c;包括产品信息、运输、支付、订单摘要和Shop Pay。 Shopify官方在去年2024年使用结账扩展取代了checkout.liquid&#xff0c;并将于2025年8月28日彻底停用checkout.liquid…

电阻的阻值识别

电阻买回来是有偏差的&#xff0c;不同的电阻种类&#xff0c;它的偏差大小会不一样&#xff0c;偏差越小的肯定越贵 主要看要求的精度要求是否越高 色环电阻或者说插件电阻 用来读数的几个色环它是比较靠近的&#xff0c;精度的色环跟用来读数的几个色环的间距会大一点点。 间…

quartz.net条件执行

quartz.net条件执行 在使用Quartz.NET时&#xff0c;你可能需要基于某些条件来决定是否执行一个任务。Quartz.NET本身并不直接支持基于条件执行任务的功能&#xff0c;但你可以通过一些策略来实现这一需求。下面是一些方法来实现基于条件的任务执行&#xff1a; 1. 使用触发器…

计算机操作系统(四) 操作系统的结构与系统调用

计算机操作系统&#xff08;四&#xff09; 操作系统的结构与系统调用 前言一、操作系统的结构1.1 简单结构1.2 模块化结构1.3 分层化结构1.4 微内核结构1.5 外核结构 二、系统调用1.1 系统调用的基本概念1.2 系统调用的类型 总结&#xff08;核心概念速记&#xff09;&#xf…

NSSCTF(MISC)——[SUCTF 2018 招新赛]single-dog

相应的做题地址&#xff1a;https://www.nssctf.cn/problem/2324 分离图片 在1.txt中得到一段颜文字 http://www.hiencode.com/aaencode.html 解密得到flag

低功耗蓝牙(BLE)方案设计实战指南

一、BLE方案设计工具链 1. 硬件选型与开发平台 TI平台&#xff1a;CC2540/CC2541芯片&#xff0c;使用SmartRF Flash Programmer烧录Nordic平台&#xff1a;nRF51822芯片&#xff0c;使用nRFgo Studio管理协议栈常用调试工具&#xff1a;TI CC Debugger、J-Link&#xff08;SW…

网络基础(一)

独立模式与网络互联 独立模式: 计算机之间相互独立。 网络互联&#xff1a;多台计算机连接在一起&#xff0c;完成数据共享。 注意&#xff1a;无论是主机内还是主机外&#xff0c;都是通过线来进行连接的&#xff0c;主机内线&#xff08;线比较短&#xff09;的连接主要考虑…

用Canvas 画布样式实现旋转的阴阳图

用Canvas 画布样式实现旋转的阴阳图 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Canvas八卦图动画</title><style>/* 重置所有元素的默认样式 */* {padding: 0;margin: 0;box-sizin…

第16届蓝桥杯单片机4T模拟赛三

本次模拟赛涉及的模块&#xff1a;基础三件套&#xff08;Led&Relay&#xff0c;按键、数码管&#xff09; 进阶单件套&#xff08;pcf8591的AD模块&#xff09; 附件&#xff1a; 各模块底层代码在文章的结尾 一、数码管部分 1.页面1 页面1要显示的格式是&#xff1a; …

优选算法的睿智之林:前缀和专题(一)

专栏&#xff1a;算法的魔法世界 个人主页&#xff1a;手握风云 目录 一、前缀和 二、例题讲解 2.1. 一维前缀和 2.2. 二维前缀和 2.3. 寻找数组的中心下标 2.4. 除自身以外数组的乘积 一、前缀和 前缀和算法是一种用于处理数组或序列数据的算法&#xff0c;其核心思想是…

瑞萨RX23E系列开发(二)建立工程

新建工程 使用倒数第二个模板 选择路径 我这里是这个型号。根据型号选择芯片 第一次需要下载FIT

【算法day19】括号生成——数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

括号生成 https://leetcode.cn/problems/generate-parentheses/description/ 数字 n 代表生成括号的对数&#xff0c;请你设计一个函数&#xff0c;用于能够生成所有可能的并且 有效的 括号组合。 左括号数必须大于右括号数&#xff0c;且小于等于n class Solution { publ…