【Spring Security】——配置器之 SecurityConfigurerAdapter 分支

官网地址 Spring Security Reference

版本:Version 5.5.0

上一篇文章中提到了配置器顶级接口 SecurityConfigurer 有三个分支,其中实现类最多的(也是最常用的)就是 SecurityConfigurerAdapter 这个分支下的配置器,这将是本章的主要内容。

概述

先简单回顾以下上一篇文章中提到的 SecurityConfigurerAdapter :

源码注释:SecurityConfigurer 的基础实现类,它允许子类只实现他们感兴趣的方法。它还提供了一种机制,用于使用 SecurityConfigurer 并在完成后获得对正在配置的 SecurityBuilder 的访问权限。

总结

  • 允许子类只实现他们感兴趣的方法。
  • and 方法可以再使用完配置器后返回正在配置的构造器。
  • setBuilder 方法是由构造器 apply 方法中自动调用的。
  • 有个复合后置处理器内部类,维护一个 List 集合,可存储多个后置处理器对象

那么它的子类都具有它的特性,并且子类之争对自己感兴趣的点进行扩展,它主要有三个分支:

  • UserDetailsAwareConfigurer
  • AbstractHttpConfigurer
  • LdapAuthenticationProviderConfigurer

在这里插入图片描述
根据这个类图可以看出来它们都有各子的侧重点:

  • AbstractHttpConfigurer 关注于配置 HttpSecurityBuilder 构造器
  • LdapAuthenticationProviderConfigurer 关注于配置 ProviderManagerBuilder 构造器
  • AbstractHttpConfigurer 也关注与于 配置 ProviderManagerBuilder 构造器 ,但是特别之处在于它还内含了 UserDetailsService 用于获取用户信息的服务类。

ProviderManagerBuilder 是用来构造 AuthenticationManager 的。

AbstractHttpConfigurer

这个抽象类很简单,官方注释也十分简介:

为在 HttpSecurity 上运行的 SecurityConfigurer 实例添加一个方便的基类。

  • HttpSecurity 是一个构造器,最终构建一个 DefaultSecurityFilterChain 类的对象。

这个基类在 AbstractHttpConfigurer 父类的基础上又扩展了两个方法:

  • disable 方法:通过删除一个配置器来实现禁用这个配置器的能力

    	public B disable() {
    		/**
    		 * 调用父类的 getBuilder 方法获取正在配置的构造器实例,
    		 * 并调用构造器的 removeConfigurer 方法,
    		 * 从构造器的配置器集合中删除当前这个配置器
    		 * 最后返回正在配置的构造器实例
    		 */ 
    		getBuilder().removeConfigurer(getClass());
    		return getBuilder();
    	}
    

    HttpSecurity (构造器)调用 removeConfigurer 方法根据 AbstractHttpConfigurer (配置器)类名删除指定的配置器。

    配置器中的 removeConfigurer 方法逻辑很简单:
    (摘自AbstractConfiguredSecurityBuilder源码)
    在这里插入图片描述

  • withObjectPostProcessor 方法:添加(追加)后置处理器对象

    	public T withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
    		/**
    		 * 向父类的复合后置处理器对象中的 List 中添加一个后置处理器
    		 */
    		addObjectPostProcessor(objectPostProcessor);
    		return (T) this;
    	}
    

    这个方法实际上调用了父类的 addObjectPostProcessor 方法,根据上一篇文章中的内容可以知道:这类构造器维护一个后置处理器链表(List),这里只是向链表中添加了一个后置处理器。

AbstractHttpConfigurer 的子类

在这里插入图片描述
上图中左侧是 HttpSecurity 的部分方法,右侧是 AbstractHttpConfigurer 的子类

可以看到,在 HttpSecurity 中可以调用各种方法将 AbstractHttpConfigurer 的子配置类应用到 HttpSecurity 上。所以说 AbstractHttpConfigurer 为在 HttpSecurity 上运行的 SecurityConfigurer 实例添加了一个方便的基类。

各个子类的内容参考:
(待补充,以后遇到一个补充一个)

UserDetailsAwareConfigurer

在这里插入图片描述

UserDetailsAwareConfigurer 和 AbstractDaoAuthenticationConfigurer

UserDetailsAwareConfigurer 是一个抽象类,允许访问 UserDetailsS​​ervice 以用作 AuthenticationManagerBuilder 的默认值。

public abstract class UserDetailsAwareConfigurer<B extends ProviderManagerBuilder<B>, U extends UserDetailsService>
		extends SecurityConfigurerAdapter<AuthenticationManager, B> {
	public abstract U getUserDetailsService();
}

它只有一个抽象方法,getUserDetailsService() 返回值是一个 UserDetailsS​​ervice 。通过泛型约束可以看出,这个分支下的所以配置类都是用来配置 ProviderManagerBuilder 的并且它们都有一个默认的 UserDetailsS​​ervice 用来获取用户的详细信息。

UserDetailsAwareConfigurer 的唯一子类 AbstractDaoAuthenticationConfigurer:
在这里插入图片描述
AbstractDaoAuthenticationConfigurer 有两个成员变量

  1. private DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
    直接为成员变量设置了初始值 DaoAuthenticationProvider 实例

    provider 成员是类的一个实例变量,不是类变量,即只有创建对应的实例时才会对它进行赋值操作

  2. private final U userDetailsService;
    这个是父类中提到的可访问的 UserDetailsService 对象,这个变量通过构造方法设置,设置后不再更改(final 修饰)。

也就是说,每一个 AbstractDaoAuthenticationConfigurer 子类都会产生一个 DaoAuthenticationProvider ,并且绑定一个 UserDetailsService 再用这个配置类配置 ProviderManagerBuilder 这样 ProviderManager 就可以管理多个认证器。

AbstractDaoAuthenticationConfigurer 中的方法都很简单,这个配置器抽象类只重写了 SecurityConfigurerAdapter 的 configure() 方法:

@Override
public void configure(B builder) throws Exception {
	provider = postProcess(provider);
	// 为将要构建的 AuthenticationManager 添加 provider
	builder.authenticationProvider(provider);
}

关于 ProviderManagerBuilder 可以参考之前的文章:【Spring Security】—— AuthenticationManagerBuilder

DaoAuthenticationConfigurer 和 UserDetailsServiceConfigurer

它们都继承了 AbstractDaoAuthenticationConfigurer ,DaoAuthenticationConfigurer 相对简单,它只有一个构造方法,通过构造方法可以指定了 UserDetailsService 实例而已,其他逻辑与父类相同:
在这里插入图片描述
UserDetailsServiceConfigurer 除了可以通过构造函数指定 UserDetailsService 实例外, 它还重写了 AbstractDaoAuthenticationConfigurer 中的 configure 方法,在调用父类的 configure 方法之前加入了 initUserDetailsService 方法,以方便自定义初始化 UserDetailsService。但是这里的 initUserDetailsService 方法是空方法,具体的实现逻辑交给子类自行实现:
在这里插入图片描述

UserDetailsManagerConfigurer

UserDetailsManagerConfigurer 继承了 UserDetailsServiceConfigurer 并重写了父类定义的 initUserDetailsService 方法,对这个空方法做了基本的实现,具体的实现逻辑就是将(内部类)UserDetailsBuilder 所构建出来的 UserDetails 以及提前准备好的 UserDetails 中的用户存储到 UserDetailsService 中。
在这里插入图片描述

它有两个成员变量,如上图中使用到的两个变量,它们用来存储准备的加入 UserDetailsService 的用户:

  1. private final List<UserDetailsBuilder> userBuilders = new ArrayList<>();
  2. private final List<UserDetails> users = new ArrayList<>();

【UserDetailsBuilder 所构建出来的 UserDetails】 和 【提前准备好的 UserDetails】都是通过 UserDetailsManagerConfigurer 的 withUser 方法添加的,这些逻辑都比较简单,可以直接看源码。

UserDetailsManagerConfigurer 有两个子类:

  1. JdbcUserDetailsManagerConfigurer
  2. InMemoryUserDetailsManagerConfigurer

它们的源码比较简单,直接点开就能明白。

LdapAuthenticationProviderConfigurer

Ldap 额外依赖

在点开 LdapAuthenticationProviderConfigurer 的源码时,会发现有很对类找不到,这是英文要使用 Ldap 认证处理 spring-security 基础依赖外,还需要添加一些 Ldap 相关的依赖:

		<!--  使用 Ldap 认证需要的依赖 start  -->
		<dependency>
		    <groupId>org.springframework.security</groupId>
		    <artifactId>spring-security-ldap</artifactId>
		</dependency>
		<dependency>
		    <groupId>com.unboundid</groupId>
		    <artifactId>unboundid-ldapsdk</artifactId>
		</dependency>
		<!--  使用 Ldap 认证需要的依赖 end  -->
Ldap 介绍

在看源码之前需要简单了解一下 Ldap 是什么?

先在百度百科上搜以下:
在这里插入图片描述

  1. LDAP 是 Light Directory Access Portocol 的缩写,中文名称:轻型目录访问协议
  2. 中立的,意思应该是说这个协议可以跨平台,于平台无关
  3. 应用协议,意思应该是说它属于应用层协议
  4. 通过IP协议控制目录信息
  5. 它是一种有层次的、树形结构

这只是简单了解了 Ldap 大概是什么,其实 Ldap 也可以理解成数据库,只是它与传统数据库的结构不同,再加上它的层次结构特性,所以它也常用做权限控制及单点登录,所以SpringSecurity中也提供这种认证方式的配置入口。

在文章开始【概述】中提到过这个类的作用是配置 ProviderManagerBuilder 构造器,而 ProviderManagerBuilder 是用于构造认证器的。所以 LdapAuthenticationProviderConfigurer 最终是用来配置一个 Ldap 认证器的配置类。

关于 Ldap 的详细内容可以参考:

LdapAuthenticationProviderConfigurer 源码

在这里插入图片描述

它有两个内部类:
  1. ContextSourceBuilder

    Allows building a BaseLdapPathContextSource and optionally creating an embedded LDAP instance.

    这个内部类的作用是构造一个 BaseLdapPathContextSource

    • 默认 Ldap 认证服务端口:33389
    • 默认 Ldap 认证服务地址:127.0.0.1
    • 默认 Ldap 目录服务的根为:dc=springframework,dc=org

    我理解 BaseLdapPathContextSource 就是一个 Ldap 环节上下文,在构建之前可以通过 ContextSourceBuilder 来设定连接信息。

  2. PasswordCompareConfigurer

    这个内部类的作用是配置密码比较器,包括加密器、密码属性名称(默认userPassword)
    但是如果调用 passwordCompare 方法(这个是 LdapAuthenticationProviderConfigurer 的方法)来获取 PasswordCompareConfigurer 进行自定义配置,在不额外设置密码属性时,默认会将密码属性改成 password 。
    在这里插入图片描述

主要方法:
  1. configure 方法
    这个不用多少,这是配置器真正完成配置的方法,由构造器构架对象的适合调用的。这里会发现,这个类没有 init 方法,这说明 Ldap 认证配置不关心初始化,初始化方法使用的是父类中的空方法。

    @Override
    public void configure(B builder) throws Exception {
    	LdapAuthenticationProvider provider = postProcess(build());
    	builder.authenticationProvider(provider);
    }
    

    这个法方法的逻辑很简单,先构建出一个 LdapAuthenticationProvider 实例,在把这个实例交给构造器。

  2. build 方法
    这个方法不要和构造器的 build 方法搞混了,它们没啥关系。这个方法在构造器的 configure 方法中被调用,作用是产生一个 LdapAuthenticationProvider 实例。

    private LdapAuthenticationProvider build() throws Exception {
    	//调用 getContextSource 获取  BaseLdapPathContextSource 实例
    	BaseLdapPathContextSource contextSource = getContextSource();
    	/**
    	 * 利用 BaseLdapPathContextSource 实例
    	 * 调用 createLdapAuthenticator 方法创建 LdapAuthenticator 实例(Ldap认证器实例)
    	 */
    	LdapAuthenticator ldapAuthenticator = createLdapAuthenticator(contextSource);
    	//调用 getLdapAuthoritiesPopulator 方法 根据成员属性获得一个 DefaultLdapAuthoritiesPopulator 实例
    	LdapAuthoritiesPopulator authoritiesPopulator = getLdapAuthoritiesPopulator();
    	
    	// 然后构造出(new) LdapAuthenticationProvider 
    	LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider(
    			ldapAuthenticator, authoritiesPopulator);
    	ldapAuthenticationProvider.setAuthoritiesMapper(getAuthoritiesMapper());
    	if (userDetailsContextMapper != null) {
    		ldapAuthenticationProvider
    				.setUserDetailsContextMapper(userDetailsContextMapper);
    	}
    	return ldapAuthenticationProvider;
    }
    

总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值