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

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

一、前言

本章主要内容是关于配置器的接口架构设计,任意找一个配置器一直往上找,就会找到配置器的顶级接口:SecurityConfigurer

查看SecurityConfigurer接口的实现类情况:

在这里插入图片描述在这里插入图片描述

AbstractHttpConfigurer 抽象类的下面可以看到所有用来配置 HttpSecurity 的配置器实现类(也是构造器)。

再通过继承关系图,看看配置器顶层的架构:

在这里插入图片描述

会发现,其中:SecurityConfigurerAdapterGlobalAuthenticationConfigurerAdapterSecurityConfigurer接口进行了实现,而WebSecurityConfigurerSecurityConfigurer接口进行了继承。

查看SecurityConfigurer源码:

源码注释:
Allows for configuring a SecurityBuilder. All SecurityConfigurer first have their init(SecurityBuilder) method invoked. After all init(SecurityBuilder) methods have been invoked, each configure(SecurityBuilder) method is invoked.
允许配置SecurityBuilder(构造器)。所有SecurityConfigurer首先调用其init(SecurityBuilderr) 方法。在调用了所有init(SecurityBuilderr) 方法之后,将调用每个configure(SecurityBuilderr) 方法。
请参阅:
AbstractConfiguredSecurityBuilder
作者:
Rob Winch
类型形参:
<O> – The object being built by the SecurityBuilder B
<O> 是被 B(继承SecurityBuilder<O>的构造器)构造出来的对象类型
<B> – The SecurityBuilder that builds objects of type O. This is also the SecurityBuilder that is being configured
<B> 是被构造对象类型O的构造器类型,也是正在配置的构造器。

特殊说明:
SecurityConfigurer 的所有实现类都是用来配置构造器的。也就是说,泛型中 O 和 B 的关系是,B 用来构造 O。而配置器的作用是配置这个构造器的,从而影响最终构造的结果。

/**
 * Allows for configuring a {@link SecurityBuilder}. All {@link SecurityConfigurer} first
 * have their {@link #init(SecurityBuilder)} method invoked. After all
 * {@link #init(SecurityBuilder)} methods have been invoked, each
 * {@link #configure(SecurityBuilder)} method is invoked.
 *
 * @param <O> The object being built by the {@link SecurityBuilder} B
 * @param <B> The {@link SecurityBuilder} that builds objects of type O. This is also the
 * {@link SecurityBuilder} that is being configured.
 * @author Rob Winch
 * @see AbstractConfiguredSecurityBuilder
 */
public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {

	/**
	 * Initialize the {@link SecurityBuilder}. Here only shared state should be created
	 * and modified, but not properties on the {@link SecurityBuilder} used for building
	 * the object. This ensures that the {@link #configure(SecurityBuilder)} method uses
	 * the correct shared objects when building. Configurers should be applied here.
	 * 
	 * 初始化SecurityBuilder(构造器)。 这里只应该创建和修改共享状态,而不是用于构建对象的SecurityBuilder(构造器)上的属性。
	 * 这可确保 configure(SecurityBuilder)方法在构建时使用正确的共享对象。 应在此处应用配置器。
	 * 
	 * @param builder
	 * @throws Exception
	 */
	void init(B builder) throws Exception;

	/**
	 * Configure the {@link SecurityBuilder} by setting the necessary properties on the
	 * 
	 * 配置SecurityBuilder(构造器)必要的属性。
	 * 
	 * {@link SecurityBuilder}.
	 * @param builder
	 * @throws Exception
	 */
	void configure(B builder) throws Exception;

}

下面我们分别对SecurityConfigurerAdapterGlobalAuthenticationConfigurerAdapterWebSecurityConfigurer三个分支进行源码分析。

二、SecurityConfigurerAdapter

源码注释:
A base class for SecurityConfigurer that allows subclasses to only implement the methods they are interested in. It also provides a mechanism for using the SecurityConfigurer and when done gaining access to the SecurityBuilder that is being configured.
SecurityConfigurer的基类,它允许子类仅实现它们感兴趣的方法。。它还提供了使用 SecurityConfigurer以及完成后获取正在配置的SecurityBuilder(构造器)的访问权限的机制。
作者:
Rob Winch, Wallace Wadge
类型形参:
<O> – The Object being built by B
<O> 是被 B 构造出来的对象类型
<B> – The Builder that is building O and is configured by SecurityConfigurerAdapter
<B> 是被构造对象类型O的构造器,同时也是此 SecurityConfigurerAdapter正在配置的对象。

/**
 * A base class for {@link SecurityConfigurer} that allows subclasses to only implement
 * the methods they are interested in. It also provides a mechanism for using the
 * {@link SecurityConfigurer} and when done gaining access to the {@link SecurityBuilder}
 * that is being configured.
 *
 * @param <O> The Object being built by B
 * @param <B> The Builder that is building O and is configured by
 * {@link SecurityConfigurerAdapter}
 * @author Rob Winch
 * @author Wallace Wadge
 */
public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>> implements SecurityConfigurer<O, B> {

	private B securityBuilder;

	private CompositeObjectPostProcessor objectPostProcessor = new CompositeObjectPostProcessor();

	@Override
	public void init(B builder) throws Exception {
	}

	@Override
	public void configure(B builder) throws Exception {
	}

	/**
	 * Return the {@link SecurityBuilder} when done using the {@link SecurityConfigurer}.
	 * This is useful for method chaining.
	 * @return the {@link SecurityBuilder} for further customizations
	 * @deprecated For removal in 7.0. Use the lambda based configuration instead.
	 */
	@Deprecated(since = "6.1", forRemoval = true)
	public B and() {
		return getBuilder();
	}

	/**
	 * Gets the {@link SecurityBuilder}. Cannot be null.
	 * @return the {@link SecurityBuilder}
	 * @throws IllegalStateException if {@link SecurityBuilder} is null
	 */
	protected final B getBuilder() {
		Assert.state(this.securityBuilder != null, "securityBuilder cannot be null");
		return this.securityBuilder;
	}

	/**
	 * Performs post processing of an object. The default is to delegate to the
	 * {@link ObjectPostProcessor}.
	 * @param object the Object to post process
	 * @return the possibly modified Object to use
	 */
	@SuppressWarnings("unchecked")
	protected <T> T postProcess(T object) {
		return (T) this.objectPostProcessor.postProcess(object);
	}

	/**
	 * Adds an {@link ObjectPostProcessor} to be used for this
	 * {@link SecurityConfigurerAdapter}. The default implementation does nothing to the
	 * object.
	 * @param objectPostProcessor the {@link ObjectPostProcessor} to use
	 */
	public void addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
		this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);
	}

	/**
	 * Sets the {@link SecurityBuilder} to be used. This is automatically set when using
	 * {@link AbstractConfiguredSecurityBuilder#apply(SecurityConfigurerAdapter)}
	 * @param builder the {@link SecurityBuilder} to set
	 */
	public void setBuilder(B builder) {
		this.securityBuilder = builder;
	}

	/**
	 * An {@link ObjectPostProcessor} that delegates work to numerous
	 * {@link ObjectPostProcessor} implementations.
	 *
	 * @author Rob Winch
	 */
	private static final class CompositeObjectPostProcessor implements ObjectPostProcessor<Object> {

		private List<ObjectPostProcessor<?>> postProcessors = new ArrayList<>();

		@Override
		@SuppressWarnings({ "rawtypes", "unchecked" })
		public Object postProcess(Object object) {
			for (ObjectPostProcessor opp : this.postProcessors) {
				Class<?> oppClass = opp.getClass();
				Class<?> oppType = GenericTypeResolver.resolveTypeArgument(oppClass, ObjectPostProcessor.class);
				if (oppType == null || oppType.isAssignableFrom(object.getClass())) {
					object = opp.postProcess(object);
				}
			}
			return object;
		}

		/**
		 * Adds an {@link ObjectPostProcessor} to use
		 * @param objectPostProcessor the {@link ObjectPostProcessor} to add
		 * @return true if the {@link ObjectPostProcessor} was added, else false
		 */
		private boolean addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
			boolean result = this.postProcessors.add(objectPostProcessor);
			this.postProcessors.sort(AnnotationAwareOrderComparator.INSTANCE);
			return result;
		}

	}

}

内容很简单:

  • 有一个内部类CompositeObjectPostProcessor:复合后置处理器对象类

  • 定义了两个成员变量:

    • 将要配置的构造器 securityBuilder
    • 复合后置处理器 objectPostProcessor
  • 从接口中实现的方法和自己新加的几个方法

    • initconfigure 是实现接口的方法
    • andgetBuilderpostProcessaddObjectPostProcessorsetBuilder 方法是自己加的

2.1 允许子类只实现他们感兴趣的方法

在源码中可以看到,所有的方法都有方法体,包括对接口方法的实现(虽然是空方法体)。所以继承 SecurityConfigurerAdapter 的配置器可以根据自己的需求实现覆盖)自己感兴趣的方法。

可以自己实现 init ,如果自己不需要初始化,也可以不实现,在构造器调用其 init 方法时什么也不做。

2.2 setBuilder 方法

这个方法有点特别,所以单独说一下,方法内容很简单,就是设置配置器的成员变量private B securityBuilder即:构造器),但是官网又这样一句注释:

Sets the SecurityBuilder to be used.
This is automatically set when using AbstractConfiguredSecurityBuilder.apply(SecurityConfigurerAdapter)

第一句很好理解:这个方法是来设置要使用的构造器(private B securityBuilder,其B extends SecurityBuilder<O>)。

第二句就是当 AbstractConfiguredSecurityBuilder.apply(SecurityConfigurerAdapter) 调用时被自动设置。

回顾上篇中的4.4.7章节:

/**
 * Applies a {@link SecurityConfigurerAdapter} to this {@link SecurityBuilder} and
 * invokes {@link SecurityConfigurerAdapter#setBuilder(SecurityBuilder)}.
 * @param configurer
 * @return the {@link SecurityConfigurerAdapter} for further customizations
 * @throws Exception
 */
@SuppressWarnings("unchecked")
public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception {
	configurer.addObjectPostProcessor(this.objectPostProcessor);
	configurer.setBuilder((B) this);
	add(configurer);
	return configurer;
}

可以看出,configurer在被调用之前是不知道要配置哪个构造器的。在构造器调用 apply 方法时才真正设置配置器的成员变量(即:构造器)。所以官方注释才说 setBuilder 方法时自动调用的,我们不能手动去设置。

只有构造器(B) this应用了这个配置器configurer,这个配置器configurer才会绑定上这个构造器(B) this

2.3 and 方法

and() 方法提供了一种使用SecurityConfigurer以及完成后获取正在配置的SecurityBuilder(构造器)的访问权限的机制。

有必要说一下为什么是正在配置,因为在这个时候还没有对构造器(private B securityBuilder )进行配置。

在上篇文章中讲解Builder设计模式时,提到过构造器的构造时机在调用构造器的 build 方法,在AbstractConfiguredSecurityBuilder#doBuild方法中加入了构造生命周期控制,在里面才开始调用各个配置器的 init 方法和 configure 方法。所以这里获取到的时正在配置的构造器对象。

在构造器的构造过程中利用配置器进行配置,从而影响最终构造的结果。

2.4 复合后置处理对象

这个内部类对象很简单,看一下内部类的内容就明白了:它维护的是一个 List 集合

private List<ObjectPostProcessor<?>> postProcessors = new ArrayList<>();

因此称之为:复合后置处理对象(CompositeObjectPostProcessor),就是里面有多个。

2.5 DEBUG 参数跟踪

AbstractConfiguredSecurityBuilder#apply

在这里插入图片描述
SecurityConfigurerAdapter#setBuilder

在这里插入图片描述

由上可知: SecurityConfigurerAdapter中设置的构造器为HttpSecurity

三、GlobalAuthenticationConfigurerAdapter

这个类的类名说明它是一个全局配置相关的类。

/**
 * A {@link SecurityConfigurer} that can be exposed as a bean to configure the global
 * {@link AuthenticationManagerBuilder}. Beans of this type are automatically used by
 * {@link AuthenticationConfiguration} to configure the global
 * {@link AuthenticationManagerBuilder}.
 *
 * @author Rob Winch
 * @since 5.0
 */
@Order(100)
public abstract class GlobalAuthenticationConfigurerAdapter
		implements SecurityConfigurer<AuthenticationManager, AuthenticationManagerBuilder> {

	@Override
	public void init(AuthenticationManagerBuilder auth) throws Exception {
	}

	@Override
	public void configure(AuthenticationManagerBuilder auth) throws Exception {
	}

}

从源码可以看出它实现 SecurityConfigurer 接口,没有具体的实现内容,只是对构造器和构造器将要构造的对象做了限制:

  • 将要配置的构造器:AuthenticationManagerBuilder

    可以作为bean公开以配置全局构造器AuthenticationManagerBuilder

  • 将要构造的对象:AuthenticationManager

    将被构造器构造出最终目的类型AuthenticationManager

四、WebSecurityConfigurer

/**
 * Allows customization to the {@link WebSecurity}. In most instances users will use
 * {@link EnableWebSecurity} and create a {@link Configuration} that exposes a
 * {@link SecurityFilterChain} bean. This will automatically be applied to the
 * {@link WebSecurity} by the {@link EnableWebSecurity} annotation.
 *
 * @author Rob Winch
 * @since 3.2
 * @see SecurityFilterChain
 */
public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends SecurityConfigurer<Filter, T> {

}

该接口继承SecurityConfigurer接口,从源码中可以看出,它没有定义自己的方法,所有的方法都是从父接口继承,那么它的作用还有配置SecurityBuilder(构造器),但是它对类型做了约束:

  • WebSecurityConfigurer 的泛型为 T

    T继承了SecurityBuilder,所以T表示是一个对象构造器

  • SecurityBuilder的泛型为Filter

    表示 SecurityBuilder 将要构造的对象是一个 javax.servet.Filter,也就是说,它约束了T,说明T的作用是构造Filter

  • 将要传入父接口SecurityConfigurer的两个泛型:FilterT

    WebSecurityConfigurer 继承 SecurityConfigurer,从这里可以看出,对SecurityConfigurer做了约束,目前只有T还没有指定具体的类型,至于最终使用什么类型的构造器由实现类或者子接口指定。


http://www.kler.cn/news/157077.html

相关文章:

  • MySQL官网推荐书籍
  • 【接口测试】POST请求提交数据的三种方式及Postman实现
  • 【广州华锐视点】机械零件拆装VR仿真教学系统
  • 【EI会议征稿】第五届人工智能与机电自动化国际学术会议(AIEA 2024)
  • bad_python
  • Vue3实现一个拾色器功能
  • TimeGPT:时间序列预测模型实例
  • TDA4开发环境Docker化
  • 《系统架构设计师教程(第2版)》第2章-计算机系统基础知识-01-计算机硬件
  • Spring中通知是什么
  • Redis7--基础篇4(Redis事务)
  • CocosCreator 面试题(二十) Cocos creator 如何实现一个置灰Shader?
  • [Ubuntu 20.04] 使用Netplan配置网络静态IP
  • RH850P1X芯片学习笔记-Pin Functions
  • 智能优化算法应用:基于松鼠算法无线传感器网络(WSN)覆盖优化 - 附代码
  • 什么是Overlay网络?Overlay网络与Underlay网络有什么区别?
  • 搭建CIG容器重量级监控平台
  • C\C++ 获取最值
  • 无人机覆盖路径规划综述
  • 学习率设置(写给自己看)
  • Redis队列stream,Redis多线程详解
  • Python3+selenium自动化测试框架详解
  • LoadBalancer将服务暴露到外部实现负载均衡Openelb-layer2模式配置介绍
  • 新数字化时代广告的多变式玩法:广告电商
  • 基于Java SSM产品销售管理系统
  • 快速安装Axure RP Extension for Chrome插件
  • Web漏洞分析-SQL注入XXE注入(上)
  • 贝叶斯网络 (人工智能期末复习)
  • 编译原理:正规表达式转换为NFA(原理+完整代码实现)
  • 零基础学Python的第五天||字符串(2)