Spring Security (二) 安全拦截器

Scroll Down

Spring Security 安全拦截器

1.概要

之前又简单介绍SpringSecurity的拦截流程,本篇文章就来深扒一下安全过滤器FilterChainProxy的底层原理

2.FilterChainProxy如何被Spring容器管理

FilterChainProxy是一个过滤器,在SpringSecurity中,FilterChainProxy是其框架的核心存在。

在SpringSecurity中,一个过滤器名字叫springSecurityFilterChain,它的类型就是FilterChainProxy。

在@EnableWebSecurity注解中,@Import了一个关键类:WebSecurityConfiguration。

该类实现了ImportAware和BeanClassLoaderAware接口,分别实现了setImportMetadata()和setBeanClassLoader()方法

image-20210724170411372

  • setImportMetadata()方法的作用是注入注解元数据。

image-20210724171238765

  • setBeanClassLoader()方法作用就是注入类加载器ClassLoader。

image-20210724171747171

在WebSecurityConfiguration的springSecurityFilterChain()方法中,FilterChainProxy被注入到Spring容器中的

image-20210724214812346

3.WebSecurityConfiguration类

在深入springSecurityFilterChain()方法底层原理之前,需要先了解WebSecurityConfiguration中几个重要的成员变量。

image-20210724215256601

4. WebSecurity类

由WebSecurity的类注释可以了解到:

  • WebSecurity是由WebSecurityConfiguration创建的,用于创建FilterChainProxy
  • FilterChainProxy在SpringSecurity中,以springSecurityFilterChainProxy的bean名称暴露于容器之中的
  • 可以通过基础抽象类WebSecurityConfigurerAdapter来定制化WebSecurity

由于@EnableWebSecurity导入了WebSecurityConfiguration,因而WebSecurity也被创建。

image-20210724220230687

WebSecurity是一个final修饰的类,即该类:

  • 不能被继承,没有子类
  • 该类方法都为final

WebSecurity继承了AbstractConfiguredSecurityBuilder<Filter, WebSecurity>。

5. AbstractConfiguredSecurityBuilder类

AbstractConfiguredSecurity是抽象类,所以有一个抽象方法performBuild()

protected abstract O performBuild() throws Exception;

这个方法会在建造FilterChainProxy时有使用到,这里先留个印象。

回到AbstractConfiguredSecurityBuilder类定义

image-20210724231022337

可以知道它继承了AbstractSecurityBuilder,但自身带的一个泛型<O, B extends SecurityBuilder>

目光聚焦到SecurityBuilder这个接口

// 安全建造者
// 顾名思义是一个builder建造者,创建并返回一个类型为O的对象
public interface SecurityBuilder<O> {
    O build() throws Exception;
}
// 抽象安全建造者
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
    private AtomicBoolean building = new AtomicBoolean();
    private O object;

	// build建造方法被final修饰,不允许子类覆盖。
    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");
    }

    // 子类需要重写doBuild()方法
    protected abstract O doBuild() throws Exception;
}

所以B extends SecurityBuilder就是指B是SecurityBuilder的子类,用于建造O。

从WebSecurity中的类定义可以发现

AbstractConfiguredSecurityBuilder<Filter, WebSecurity>

AbstractConfiguredSecurityBuilder作用就是通过WebSecurity这个建造者建造出一个Filter,而这个Filter就是FilterChainProxy

因此O就是指FilterChainProxy,B就是指的安全建造者WebSecurity

再来看下AbstractConfiguredSecurityBuilder的成员变量。

image-20210724231322646

6.SecurityConfigurer类

SecurityConfigurer是一个接口,它就是指的安全配置器,看下它的类定义信息。

  • SecurityConfigurer允许用于配置安全建造者(SecurityBuilder)
  • 安全建造者(SecurityBuilder)在初始化时调用init方法,同时会初始化所有的SecurityConfigurer
  • 安全建造者(SecurityBuilder)在调用configure方法时,同时会调用所有SecurityConfigurer的configure

image-20210724231840424

SecurityConfigurer<O, B extends SecurityBuilder>

这里同上,O指的FilterChainProxy,B指的SecurityBuilder。

那么这里的SecurityConfigurer就是用于向FilterChainProxy中添加过滤器。

private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();

所以configurers就是以建造者为key,各种配置类为value的一个LinkedHashMap。

7.doBuild()方法

下面来看下AbstractConfiguredSecurityBuilder的doBuild()方法。doBuild()方法在之前提到过,它是AbstractSecurityBuilder抽象出来的方法,由AbstractConfiguredSecurityBuilder类来实现。这个方法会通过使用SecurityConfigurer去执行建造逻辑,建造逻辑主要分为五大步:

  1. 执行初始化前的所需做的额外工作(beforeInit())
  2. 执行初始化(init())
  3. 执行配置前所需的额外工作(beforeConfigure())
  4. 执行配置调用(configure)
  5. 执行建造(performBuild())
@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;
	}
}

performBuild()方法是AbstractConfiguredSecurityBuilder提供的抽象方法,具体有其子类:HttpSecurity和WebSecurity来实现。

8.WebSecurity什么时候被创建的?

答案就是在WebSecurityConfiguration的setFilterChainProxySecurityConfiguere()方法里。

image-20210724233035253

方法中的webSecurityConfigurers是通过了@Value注解来注入的bean集合,@Value表达式中又包含了一个autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()的调用。

进入AutowiredWebSecurityConfigurersIgnoreParents类,查看其方法getWebSecurityConfigurers()方法。

image-20210724233355031

总结

全文讲解了FilterChainProxy以及WebSecurityConfiguration,但是对于doBuild()方法没有进行细致的分析讲解,由于doBuild()的逻辑比较复杂,将在下一章进行细致分析。

在WebSecurityConfiguration中,需要关注两个方法:

  1. springSecurityFilterChain()方法
  2. setFilterChainProxySecurityConfigurer()方法

springSecurityFilterChain方法用于通过安全建造器WebSecurity来建造出FilterChainProxy。

setFilterChainProxySecurityConfigurer()方法是用于创建出安全建造起WebSecurity。

在SpringSecurity中,对于WebSecurity,使用了建造者设计模式。

参考

以及感谢CoderBruis 主流框架分析 :https://github.com/coderbruis/JavaSourceCodeLearning