Spring Security 6.x 系列(9)—— 基于过滤器链的源码分析(二)

一、前言

在本系列文章:

Spring Security 6.x 系列(4)—— 基于过滤器链的源码分析(一)中着重分析了Spring SecuritySpring Boot 的自动配置、 DefaultSecurityFilterChain 的构造流程、FilterChainProxy 的构造流程。

Spring Security 6.x 系列(7)—— 源码分析之Builder设计模式中详细分析了Spring SecurityBuilder设计模式和WebSecurityHttpSecurityAuthenticationManagerBuilder 这三个重要构造者公共的部分。

Spring Security 6.x 系列(8)—— 源码分析之配置器SecurityConfigurer接口及其分支实现中分析SecurityConfigurer接口及其分支实现。

今天沿着Spring Boot自动配置中对未被介绍的@EnableGlobalAuthentication进行分析展开。

二、AuthenticationConfiguration

@EnableWebSecurity注解中引入了@EnableGlobalAuthentication注解:

在这里插入图片描述
@EnableGlobalAuthentication注解上使用@Import(AuthenticationConfiguration.class)注解引入本类:

在这里插入图片描述
我们先看看里面有些什么?

在这里插入图片描述

2.1 成员变量

// 状态标志位,AuthenticationManager是否正处于构建过程中
private AtomicBoolean buildingAuthenticationManager = new AtomicBoolean();
// Application容器
private ApplicationContext applicationContext;
// 用于记录所要构建的AuthenticationManager 
private AuthenticationManager authenticationManager;
// AuthenticationManager是否已经被构建的标志
private boolean authenticationManagerInitialized;
// 全局认证配置适配器列表
private List<GlobalAuthenticationConfigurerAdapter> globalAuthConfigurers = Collections.emptyList();
// 对象后处理器
private ObjectPostProcessor<Object> objectPostProcessor;

2.2 核心内容

2.2.1 authenticationManagerBuilder 方法

实例化一个AuthenticationManagerBuilder类型的构造者,用于构造AuthenticationManager实例:

@Bean
public AuthenticationManagerBuilder authenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor,ApplicationContext context) {/*** Lazy密码加密器:该对象创建时容器中可能还不存在真正的密码加密器* 但是用该lazy密码加密器进行加密或者密码匹配时,会从容器中获取类型为PasswordEncoder的密码加密器,* 如果容器中不存在类型为PasswordEncoder的密码加密器,则使用* PasswordEncoderFactories.createDelegatingPasswordEncoder()创建一个PasswordEncoder供随后加密或者密码匹配使用* LazyPasswordEncoder是定义在当前配置类中的一个内部类*/LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);/***获取认证事件的发布器*/AuthenticationEventPublisher authenticationEventPublisher = getAuthenticationEventPublisher(context);/*** 生成AuthenticationManagerBuilder实例,使用实现类为DefaultPasswordEncoderAuthenticationManagerBuilder* DefaultPasswordEncoderAuthenticationManagerBuilder是定义在该配置类中的一个内部类,它继承自AuthenticationManagerBuilder* 是SpringSecurity缺省使用的 AuthenticationManagerBuilder实现类*/DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, defaultPasswordEncoder);/***如果有事件发布器,则设置*/if (authenticationEventPublisher != null) {result.authenticationEventPublisher(authenticationEventPublisher);}return result;
}private AuthenticationEventPublisher getAuthenticationEventPublisher(ApplicationContext context) {if (context.getBeanNamesForType(AuthenticationEventPublisher.class).length > 0) {return context.getBean(AuthenticationEventPublisher.class);}return this.objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());
}

关于AuthenticationManagerBuilder在下文会详细介绍。

2.2.2 getAuthenticationManager 方法

根据配置生成认证管理器 AuthenticationManager,该方法具有幂等性且进行了同步处理 。

首次调用会触发真正的构建过程生成认证管理器 AuthenticationManager,再次的调用都会返回首次构建的认证管理器 AuthenticationManager

public AuthenticationManager getAuthenticationManager() throws Exception {// authenticationManager如果已经被构建则直接返回authenticationManagerif (this.authenticationManagerInitialized) {return this.authenticationManager;}// 获取容器中的AuthenticationManagerBuilder实例用于创建AuthenticationManagerAuthenticationManagerBuilder authBuilder = this.applicationContext.getBean(AuthenticationManagerBuilder.class);// 如果已经正在使用authBuilder进行构建, 则这里直接返回一个包装了构建器authBuilder的AuthenticationManagerDelegator对象// true表示现在正在构建过程中,false表示现在不在构建过程中if (this.buildingAuthenticationManager.getAndSet(true)) {return new AuthenticationManagerDelegator(authBuilder);}// 将全局配置设置到AuthenticationManagerBuilder中for (GlobalAuthenticationConfigurerAdapter config : this.globalAuthConfigurers) {authBuilder.apply(config);}// 构建AuthenticationManagerthis.authenticationManager = authBuilder.build();/*** 	如果容器中没有用于构建 AuthenticationManager的 AuthenticationProvider bean供authBuilder使用,也没有为 authBuilder 置 parent AuthenticationManager时,*  则上面声明的 authenticationManager为 null。不过这种情况缺省情况下并不会发生:*  因为该配置类中 bean InitializeUserDetailsBeanManagerConfigurer为 authBuilder添加的 InitializeUserDetailsBeanManagerConfigurer *  会在这种情况下构造一个 DaoAuthenticationProvider对象给 authBuilder使用。*  另外,一般情况下,开发人员也会提供自己的 AuthenticationProvider 实现类。*  通常经过上面的 authBuilder.build(),authenticationManager 对象都会被创建,*  但是如果 authenticationManager 未被创建,这里尝试使用 getAuthenticationManagerBean()再次设置 authenticationManage*/if (this.authenticationManager == null) {this.authenticationManager = getAuthenticationManagerBean();}//将authenticationManagerInitialized 设置为true,说明authenticationManager已经初始化完成this.authenticationManagerInitialized = true;// 返回构建好的AuthenticationManagerreturn this.authenticationManager;
}

2.2.3 初始化GlobalAuthenticationConfigurerAdapter的三个实现

定义一个EnableGlobalAuthenticationAutowiredConfigurer,他会加载使用了注解@EnableGlobalAuthenticationBean,用于配置全局AuthenticationManagerBuilder

@Bean
public static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(ApplicationContext context) {return new EnableGlobalAuthenticationAutowiredConfigurer(context);
}private static class EnableGlobalAuthenticationAutowiredConfigurer extends GlobalAuthenticationConfigurerAdapter {private final ApplicationContext context;private static final Log logger = LogFactory.getLog(EnableGlobalAuthenticationAutowiredConfigurer.class);EnableGlobalAuthenticationAutowiredConfigurer(ApplicationContext context) {this.context = context;}@Overridepublic void init(AuthenticationManagerBuilder auth) {Map<String, Object> beansWithAnnotation = this.context.getBeansWithAnnotation(EnableGlobalAuthentication.class);if (logger.isTraceEnabled()) {logger.trace(LogMessage.format("Eagerly initializing %s", beansWithAnnotation));}}}

定义一个InitializeUserDetailsBeanManagerConfigurer配置类,用于配置单例的UserDetailsService时延时配置全局AuthenticationManagerBuilder

@Bean
public static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(ApplicationContext context) {// InitializeUserDetailsBeanManagerConfigurer是GlobalAuthenticationConfigurerAdapter的一个实现return new InitializeUserDetailsBeanManagerConfigurer(context);
}

定义一个InitializeAuthenticationProviderBeanManagerConfigurer配置类,用于配置单例的UserDetailsService时延时加载全局AuthenticationProvider

@Bean
public static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(ApplicationContext context) {// InitializeAuthenticationProviderBeanManagerConfigurer是GlobalAuthenticationConfigurerAdapter的一个实现return new InitializeAuthenticationProviderBeanManagerConfigurer(context);
}

2.2.4 AuthenticationManagerDelegator

AuthenticationManagerDelegatorAuthenticationManager的一个包装类或是委托类,主要是为了防止在初始化AuthenticationManager时发生无限递归:

  • 当这个内部类被构建时,会注入一个AuthenticationManagerBuilder实例。
  • authenticate()方法具有幂等性且进行了同步处理
    • 当这个类的authenticate()方法被第一次调用时会使用AuthenticationManagerBuilder创建一个AuthenticationManager保存到这个类的delegate属性中,同时将delegateBuilder置空,然后将实际鉴权处理交给AuthenticationManager
    • 后续再调用authenticate()方法就只是使用已经创建好的AuthenticationManager实例。
static final class AuthenticationManagerDelegator implements AuthenticationManager {private AuthenticationManagerBuilder delegateBuilder;private AuthenticationManager delegate;private final Object delegateMonitor = new Object();// 初始化一个AuthenticationManagerBuilder实例AuthenticationManagerDelegator(AuthenticationManagerBuilder delegateBuilder) {Assert.notNull(delegateBuilder, "delegateBuilder cannot be null");this.delegateBuilder = delegateBuilder;}// 具有幂等性且进行了同步处理@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {// 如果已经包含创建成功的AuthenticationManager,直接调用AuthenticationManager.authenticate()方法返回一个Authenticationif (this.delegate != null) {return this.delegate.authenticate(authentication);}// 如果没有包含创建成功的AuthenticationManager,进入同步方法synchronized (this.delegateMonitor) {if (this.delegate == null) {// 使用AuthenticationManagerBuilder构建一个AuthenticationManager,// 将值设置到AuthenticationManagerDelegator的delegate属性this.delegate = this.delegateBuilder.getObject();this.delegateBuilder = null;}}// 调用AuthenticationManager.authenticate()方法返回一个Authenticationreturn this.delegate.authenticate(authentication);}@Overridepublic String toString() {return "AuthenticationManagerDelegator [delegate=" + this.delegate + "]";}
}

三、AuthenticationManagerBuilder

3.1 继承关系

在这里插入图片描述
在前面文章了解过 WebSecurityHttpSecurityAuthenticationManagerBuilder 这三个重要构造者有一条继承树:

|- SecurityBuilder|- AbstractSecurityBuilder|- AbstractConfiguredSecurityBuilder

3.2 ProviderManagerBuilder

ProviderManagerBuilder是一个接口,继承 SecurityBuilder ,也是一个构造器,它构造的对象是 AuthenticationManager(认证管理器 ):

源码注释:
用于执行创建 ProviderManagerSecurityBuilder(构造器) 接口

/*** Interface for operating on a SecurityBuilder that creates a {@link ProviderManager}** @param <B> the type of the {@link SecurityBuilder}* @author Rob Winch*/
public interface ProviderManagerBuilder<B extends ProviderManagerBuilder<B>>extends SecurityBuilder<AuthenticationManager> {/*** Add authentication based upon the custom {@link AuthenticationProvider} that is* passed in. Since the {@link AuthenticationProvider} implementation is unknown, all* customizations must be done externally and the {@link ProviderManagerBuilder} is* returned immediately.** Note that an Exception is thrown if an error occurs when adding the* {@link AuthenticationProvider}.* @return a {@link ProviderManagerBuilder} to allow further authentication to be* provided to the {@link ProviderManagerBuilder}*/B authenticationProvider(AuthenticationProvider authenticationProvider);}

它只有两个抽象方法:

  • build() 方法继承自父类,用来构建AuthenticationManager对象

  • authenticationProvider(AuthenticationProvider authenticationProvider) 方法

    这个方法由子类实现,源码对这个方法的描述:

    • 根据传入的自定义 AuthenticationProvider添加身份验证(authentication )。
    • 由于 AuthenticationProvider 实现未知,因此所有自定义都必须在外部完成并立即返回
      ProviderManagerBuilder
    • 添加过程中出错会抛异常。

个人理解:
这个接口对父接口扩展的主要点在于以authenticationProvider(AuthenticationProvider authenticationProvider) 方法的形式向构造器添加自定义的认证提供者(AuthenticationProvider),每次添加完之后会立即返回添加完认证提供者之后的构造器,这样就可以利用这个返回对象继续添加向其认证提供者(AuthenticationProvider)。

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

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

相关文章

详解原生Spring当中的事务

&#x1f609;&#x1f609; 学习交流群&#xff1a; ✅✅1&#xff1a;这是孙哥suns给大家的福利&#xff01; ✨✨2&#xff1a;我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料 &#x1f96d;&#x1f96d;3&#xff1a;QQ群&#xff1a;583783…

Spring Security 6.x 系列(8)—— 源码分析之配置器SecurityConfigurer接口及其分支实现

一、前言 本章主要内容是关于配置器的接口架构设计&#xff0c;任意找一个配置器一直往上找&#xff0c;就会找到配置器的顶级接口&#xff1a;SecurityConfigurer。 查看SecurityConfigurer接口的实现类情况&#xff1a; 在 AbstractHttpConfigurer 抽象类的下面可以看到所有…

idea类和方法模版

类模版 修改目标位置 class #if (${PACKAGE_NAME} && ${PACKAGE_NAME} ! "")package ${PACKAGE_NAME};#end #parse("File Header.java")/*** ${Description}* author whc ${YEAR}/${MONTH}/${DAY}* version v1.0 */public class ${NAME} { }inte…

【网络安全】虚假IP地址攻击如何防范?

在当今的网络时代&#xff0c;虚假IP地址攻击已成为一种新型的网络攻击方式&#xff0c;给网络安全带来了极大的威胁。那么&#xff0c;什么是虚假IP地址攻击&#xff1f;又如何进行溯源和防范呢&#xff1f;本文将为您揭开这一神秘面纱。 一、虚假IP地址攻击概述 虚假IP地址攻…

[python]离线加载fetch_20newsgroups数据集

首先手动下载这个数据包 http://qwone.com/~jason/20Newsgroups/20news-bydate.tar.gz 下载这个文件后和脚本放一起就行&#xff0c;然后 打开twenty_newsgroups.py文件&#xff08;在fetch_20newsgroups函数名上&#xff0c;右键转到定义即可找到&#xff09; 之后运行代码即…

羊大师教你如何有效应对冬季流感,保护自己与家人

羊大师教你如何有效应对冬季流感&#xff0c;保护自己与家人 随着冬季的临近&#xff0c;流感病毒将再次蔓延。如何预防冬季流感来袭&#xff0c;成为了许多人关注的话题。幸运的是&#xff0c;我们可以采取一系列的预防措施来保护自己和家人&#xff0c;避免被流感侵袭。下面…

【Altium designer 20】

Altium designer 20 1. Altium designer 201.1 原理图库1.1.1 上划岗 在字母前面加\在加字母1.1.2 自定义快捷键1.1.3 对齐1.1.4 在原有的电路图中使用封装1.1.5 利用excel创建IC类元件库1.1.6 现有原理图库分类以及调用1.1.7 现有原理图库中自动生成原理图库 1.2 绘制原理图1.…

【初阶解法-数据结构】包含min函数的栈(代码+图示)

【数据结构】刷题-包含min函数的栈(代码图示)-初阶解法 文章目录 【数据结构】刷题-包含min函数的栈(代码图示)-初阶解法题目提炼题目要求分析题目总结思路代码时间/空间复杂度进阶版 题目 定义栈的数据结构&#xff0c;请在该类型中实现一个能够得到栈中所含最小元素的 min 函…

Ubuntu22.04 交叉编译mp4V2 for Rv1106

一、配置工具链环境 sudo vim ~/.bashrc在文件最后添加 export PATH$PATH:/opt/arm-rockchip830-linux-uclibcgnueabihf/bin 保存&#xff0c;重启机器 二、下载mp4v2 下载路径&#xff1a;MP4v2 | mp4v2 三、修改CMakeLists.txt 四、执行编译 mkdir build cd buildcmak…

羊大师教你如何在冬天运动,然后悄悄惊艳所有人

羊大师教你如何在冬天运动&#xff0c;然后悄悄惊艳所有人 寒冷的冬季&#xff0c;寂静的清晨&#xff0c;你是否也曾感到在冰冷的天气中进行锻炼是一件非常困难的事情&#xff1f;但是&#xff0c;现在请跟随小编羊大师一起来探索冬季秘密运动&#xff0c;让你在春节惊艳众人…

人工智能_机器学习060_核函数对应数学公式_数据空间错位分割_简单介绍_以及核函数总结---人工智能工作笔记0100

我们之前做的都是线性分类问题,那么需要一根线来分割类别,但是,如果出现了,环形数据,我们知道,在二维中我们就无法分割了,那么有没有什么办法分割呢? 实际上是有的,可以看到,我们可以把数据进行升维,可以看到,如果把数据升高到2维度以上,可以看到,神奇的一幕出现了,这个时候,因…

【Linux】进程控制--进程创建/进程终止/进程等待/进程程序替换/简易shell实现

文章目录 一、进程创建1.fork函数2.fork函数返回值3.写时拷贝4.fork常规用法5.fork调用失败的原因 二、进程终止1.进程退出码2.进程退出场景3.进程常见退出方法 三、进程等待1.为什么要进行进程等待2.如何进行进程等待1.wait方法2.waitpid方法3.获取子进程status4.进程的阻塞等…

价差后的几种方向,澳福如何操作才能盈利

在价差出现时&#xff0c;澳福认为会出现以下几种方向。 昂贵资产的贬值和便宜资产的平行升值。昂贵的资产贬值&#xff0c;而便宜的资产保持不变。昂贵资产的贬值和便宜资产的平行贬值&#xff0c;但昂贵资产的贬值速度更快&#xff0c;超过便宜资产。更贵的一对的进一步升值和…

7. 系统信息与系统资源

7. 系统信息与系统资源 1. 系统信息1.1 系统标识 uname()1.2 sysinfo()1.3 gethostname()1.4 sysconf() 2. 时间、日期2.1 Linux 系统中的时间2.1.1 Linux 怎么记录时间2.1.2 jiffies 的引入 2.2 获取时间 time/gettimeofday2.2.1 time()2.2.2 gettimeofday() 2.3 时间转换函数…

登录校验过滤器

会话技术 JWT令牌 过滤器Filter 拦截器 interceptor cookise package com.it.controller;import com.it.pojo.Result; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.Re…

Android wifi连接和获取IP分析

wifi 连接&获取IP 流程图 代码流程分析 一、关联阶段 1. WifiSettings.submit – > WifiManager WifiSettings 干的事情比较简单&#xff0c;当在dialog完成ssid 以及密码填充后&#xff0c;直接call WifiManager save 即可WifiManager 收到Save 之后&#xff0c;就开…

新的 BLUFFS 攻击导致蓝牙连接不再私密

蓝牙是一种连接我们设备的低功耗无线技术&#xff0c;有一个新的漏洞需要解决。 中间的攻击者可以使用新的 BLUFFS 攻击轻松窥探您的通信。 法国研究中心 EURECOM 的研究员 Daniele Antonioli 演示了六种新颖的攻击&#xff0c;这些攻击被定义为 BLUFFS&#xff08;蓝牙转发和…

【Java】文件I/O-文件内容操作-输入输出流-Reader/Writer/InputStream/OutputStream四种流

导读 在文件I/O这一节的知识里&#xff0c;对文件的操作主要分为两大类&#xff1a; ☑️针对文件系统进行的操作 ☑️针对文件内容进行的操作 上文已经讲了针对文件系统即File类的操作&#xff0c;这篇文章里博主就来带了解针对文件内容的操作&#xff0c;即输入输出流&am…

Open Inventor 2023.2.1 Crack

Fixed Bugs List 2023.2 2023.2.1 Open Inventor 2023.2.1 MeshViz #OIV-4824 Crash in MeshViz PbNonLinearDataMapping::computeColor Cache #OIV-4867 SoText3 : Texture read access violation – CAS-44904 Core #OIV-4725 Invalid displayed PoCircle color…

MySQL笔记-第06章_多表查询

视频链接&#xff1a;【MySQL数据库入门到大牛&#xff0c;mysql安装到优化&#xff0c;百科全书级&#xff0c;全网天花板】 文章目录 第06章_多表查询1. 一个案例引发的多表连接1.1 案例说明1.2 笛卡尔积&#xff08;或交叉连接&#xff09;的理解1.3 案例分析与问题解决 2. …