过滤器初始化流程分析

Spring Security 初始化流程整体上来说理解起来并不难,但是这里涉及许多零碎的知识点,把这些零碎的知识点搞懂了,再来梳理初始化流程就会容易很多。因此,这里先介绍一下 Spring Security 中一些常见的关键组件,在理解这些组件的基础上,再来分析初始化流程,就能加深对其的理解。

ObjectPostProcessor

ObjectPostProcessor 是 Spring Security 中使用频率最高的组件之一,它是一个对象后置处理器,也就是当一个对象创建成功后,如果还有一些额外的事情需要补充,那么可以通过 ObjectPostProcessor 来进行处理。这个接口中默认只有一个方法 postProcess,该方法用来完成对对象的二次处理,代码如下:

public interface ObjectPostProcessor<T> {

	<O extends T> O postProcess(O object);
}

ObjectPostProcessor 默认有两个继迷承类,如图4-1所示

image 2024 04 11 14 13 39 678
Figure 1. 图4-1 ObjectPostProcessor 的继承类
  • AutowireBeanFactoryObjectPostProcessor:由于 Spring Security 中大量采用了 Java 配置,许多过滤器都是直接 new 出来的,这些直接 new 出来的对象并不会自动注入到 Spring 容器中。Spring Security 这样做的本意是为了简化配置,但是却带来了另外一个问题就是,大量 new 出来的对象需要我们手动注册到 Spring 容器中去。AutowireBeanFactoryObjectPostProcessor 对象所承担的就是这件事,一个对象 new 出来之后,只要调用 AutowireBeanFactoryObjectPostProcessor#postProcess 方法,就可以成功注入到 Spring 容器中,它的实现原理就是通过调用 Spring 容器中的 AutowireCapableBeanFactory 对象将一个 new 出来的对象注入到 Spring 容器中去。

  • CompositeObjectPostProcessor:这是 ObjectPostProcessor 的另一个实现,一个对象可以有一个后置处理器,开发者也可以自定义多个对象后置处理器。CompositeObjectPostProcessor 是一个组合的对象后置处理器,它里边维护了一个 List 集合,集合中存放了某一个对象的所有后置处理器,当需要执行对象的后置处理器时,会遍历集合中的所有 ObjectPostProcessor 实例,分别调用实例的 postProcess 方法进行对象后置处理。在 Spring Security 框架中,最终使用的对象后置处理器其实就是 CompositeObjectPostProcessor,它里边的集合默认只有一个对象,就是 AutowireBeanFactoryObjectPostProcessor。

在 Spring Security 中,开发者可以灵活地配置项目中需要哪些 Spring Security 过滤器,一旦选定过滤器之后,每一个过滤器都会有一个对应的配置器,叫作 xxxConfigurer(例如 CorsConfigurer、CsrfConfigurer 等),过滤器都是在 xxxConfigurer 中 new 出来的,然后在 postProcess 方法中处理一遍,就将这些过滤器注入到 Spring 容器中了。

这是对象后置处理器 ObjectPostProcessor 的主要作用。

SecurityFilterChain

从名称上可以看出,SecurityFilterChain 就是 Spring Security 中的过滤器链对象。下面来看 SecurityFilterChain 的源码:

public interface SecurityFilterChain {

	boolean matches(HttpServletRequest request);

	List<Filter> getFilters();
}

可以看到,SecurityFilterChain 中有两个方法:

  1. matches:该方法用来判断 request 请求是否应该被当前过滤器链所处理。

  2. getFilters:该方法返回一个 List 集合,集合中存放的就是 Spring Security 中的过滤器。换言之,如果 matches 方法返回 true,那么 request 请求就会在 getFilters 方法所返回的 Filter 集合中被处理。

SecurityFilterChain 只有一个默认的实现类就是 DefaultSecurityFilterChain,其中定义了两个属性,并具体实现了 SecurityFilterChain 中的两个方法:

public final class DefaultSecurityFilterChain implements SecurityFilterChain {
	private static final Log logger = LogFactory.getLog(DefaultSecurityFilterChain.class);
	private final RequestMatcher requestMatcher;
	private final List<Filter> filters;

	public DefaultSecurityFilterChain(RequestMatcher requestMatcher, Filter... filters) {
		this(requestMatcher, Arrays.asList(filters));
	}

	public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters) {
		logger.info("Creating filter chain: " + requestMatcher + ", " + filters);
		this.requestMatcher = requestMatcher;
		this.filters = new ArrayList<>(filters);
	}

	public RequestMatcher getRequestMatcher() {
		return requestMatcher;
	}

	public List<Filter> getFilters() {
		return filters;
	}

	public boolean matches(HttpServletRequest request) {
		return requestMatcher.matches(request);
	}

	@Override
	public String toString() {
		return "[ " + requestMatcher + ", " + filters + "]";
	}
}

可以看到,在 DefaultSecurityFilterChain 的构造方法中,需要传入两个对象,一个是请求匹配器 requestMatcher,另一个则是过滤器集合或者过滤器数组 filters。这个实现类比较简单,这单就不再资述。

需要注意的是,在一个 Spring Security 项目中,SecurityFilterChain 的实例可能会有多个,我 们在本章后面的小节中会详细分析,并演示多个 SecurityFilterChain 实例的情况。

SecurityBuilder

Spring Security 中所有需要构建的对象都可以通过 SecurityBuilder 来实现,默认的过滤器链、代理过滤器、AuthenticationManager 等,都可以通过 SecurityBuilder 来构建。SecurityBuilder 的实现类如图4-2所示。

image 2024 04 11 14 53 36 829
Figure 2. 图4-2 SecurityBuilder继承关系图

SecurityBuilder

我们先来看 SecurityBuilder 的源码:

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

由上述代码可以看到,SecurityBuilder 中只有一个 build 方法,就是对象构建方法。build 方法的返回值,就是具体构建的对象泛型 O,也就是说不同的 SecurityBuilder 将来会构建出不同的对象。

HttpSecurityBuilder

HttpSecurityBuilder 是用来构建 HttpSecurity 对象的,HttpSecurityBuilder 的定义如下:

public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> extends
		SecurityBuilder<DefaultSecurityFilterChain> {

	<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C getConfigurer(
			Class<C> clazz);

	<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C removeConfigurer(
			Class<C> clazz);

	<C> void setSharedObject(Class<C> sharedType, C object);

	<C> C getSharedObject(Class<C> sharedType);

	H authenticationProvider(AuthenticationProvider authenticationProvider);

	H userDetailsService(UserDetailsService userDetailsService) throws Exception;

	H addFilterAfter(Filter filter, Class<? extends Filter> afterFilter);

	H addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter);

	H addFilter(Filter filter);
}

我们简单分析一下这段源码:

  1. HttpSecurityBuilder 对象本身在定义时就有一个泛型,这个泛型是 HttpSecurityBuilder 的子类,由于默认情况下 HttpSecurityBuilder 的实现类只有一个 HttpSecurity,所以可以暂且把接口中的 H 都当成 HttpSecurity 来理解。

  2. HttpSecurityBuilder 继承自 SecurityBuilder 接口,同时也指定了 SecurityBuilder 中的泛型为 DefaultSecurityFilterChain,也就是说,HttpSecurityBuilder 最终想要构建的对象是 DefaultSecurityFilterChain。

  3. getConfigurer 方法用来获取一个配置器,所谓的配置器就是 xxxConfigurer,我们将在下一小节中详细介绍配置器。

  4. removeConfigurer 方法用来移除一个配置器(相当于从 Spring Security 过滤器链中移除一个过滤器)。

  5. setSharedObject/getSharedObject 这两个方法用来设置或者获取一个可以在多个配置器之间共享的对象。

  6. authenticationProvider 方法可以用来配置一个认证器 AuthenticationProvider。

  7. userDetailsService 方法可以用来配置一个数据源 UserDetailsService。

  8. addFilterAfter/addFilterBefore 方法表示在某一个过滤器之后或者之前添加一个自定义的过滤器。

  9. addFilter 方法可以添加一个过滤器,这个过滤器必须是 Spring Security 框架提供的过滤器的一个实例或者其扩展,添加完成后,会自动进行过滤器的排序。

AbstractSecurityBuilder

AbstractSecurityBuilder 实现了 SecurityBuilder 接口,并对 build 做了完善,确保只 build 一次。我们来看一下 AbstractSecurityBuilder 源码:

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;
}

由上述代码可以看到,在 AbstractSecurityBuilder 类中:

  1. 首先声明了 building 变量,可以确保即使在多线程环境下,配置类也只构建一次。

  2. 对 build 方法进行重写,并且设置为 final,这样在 AbstractSecurityBuilder 的子类中将不能再次重写 build 方法。在 build 方法内部,通过 building 变量来控制配置类只构建一次具体的构建工作则交给 doBuild 方法去完成。

  3. getObject 方法用来返回构建的对象。

  4. doBuild方法则是具体的构建方法,该方法在 AbstractSecurityBuilder 中是一个抽象方法,具体的实现在其子类中。

一言以蔽之,AbstractSecurityBuilder 的作用是确保目标对象只被构建一次。

AbstractConfiguredSecurityBuilder

AbstractConfiguredSecurityBuilder 类的源码就稍微长一点,我们分别来看。

首先在 AbstractConfiguredSecurityBuilder 中声明了一个枚举类,用来描述构建过程的不同状态:

private enum BuildState {

    UNBUILT(0),

    INITIALIZING(1),

    CONFIGURING(2),

    BUILDING(3),

    BUILT(4);

    private final int order;

    BuildState(int order) {
        this.order = order;
    }

    public boolean isInitializing() {
        return INITIALIZING.order == order;
    }

    public boolean isConfigured() {
        return order >= CONFIGURING.order;
    }
}

可以看到,整入构建过程一共有五种不同的状态:

  • UNBUILT:配置类构建前。

  • INITIALIZING:初始化中(初始化完成之前是这个状态)。

  • CONFIGURING:配置中(开始构建之前是这个状态)。

  • BUILDING:构建中。

  • BUILT:构建完成。

这个枚举类里边还提供了两个判断方法,isInitializing 表示是否正在初始化中,isConfigured 方法表示是否己完成配置。

AbstractConfiguredSecurityBuilder 中还声明了 configurers变量,用来保存所有的配置类。针对 configurers 变量,我们可以进行添加配置、移除配置等操作,相关方法如下:

public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
        extends AbstractSecurityBuilder<O> {
    private final Log logger = LogFactory.getLog(getClass());

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

    @SuppressWarnings("unchecked")
    public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer)
            throws Exception {
        configurer.addObjectPostProcessor(objectPostProcessor);
        configurer.setBuilder((B) this);
        add(configurer);
        return configurer;
    }

    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);
            }
        }
    }

    @SuppressWarnings("unchecked")
    public <C extends SecurityConfigurer<O, B>> List<C> getConfigurers(Class<C> clazz) {
        List<C> configs = (List<C>) this.configurers.get(clazz);
        if (configs == null) {
            return new ArrayList<>();
        }
        return new ArrayList<>(configs);
    }

    @SuppressWarnings("unchecked")
    public <C extends SecurityConfigurer<O, B>> List<C> removeConfigurers(Class<C> clazz) {
        List<C> configs = (List<C>) this.configurers.remove(clazz);
        if (configs == null) {
            return new ArrayList<>();
        }
        return new ArrayList<>(configs);
    }

    @SuppressWarnings("unchecked")
    public <C extends SecurityConfigurer<O, B>> C getConfigurer(Class<C> clazz) {
        List<SecurityConfigurer<O, B>> configs = this.configurers.get(clazz);
        if (configs == null) {
            return null;
        }
        if (configs.size() != 1) {
            throw new IllegalStateException("Only one configurer expected for type "
                    + clazz + ", but got " + configs);
        }
        return (C) configs.get(0);
    }

    @SuppressWarnings("unchecked")
    public <C extends SecurityConfigurer<O, B>> C removeConfigurer(Class<C> clazz) {
        List<SecurityConfigurer<O, B>> configs = this.configurers.remove(clazz);
        if (configs == null) {
            return null;
        }
        if (configs.size() != 1) {
            throw new IllegalStateException("Only one configurer expected for type "
                    + clazz + ", but got " + configs);
        }
        return (C) configs.get(0);
    }

    private Collection<SecurityConfigurer<O, B>> getConfigurers() {
        List<SecurityConfigurer<O, B>> result = new ArrayList<>();
        for (List<SecurityConfigurer<O, B>> configs : this.configurers.values()) {
            result.addAll(configs);
        }
        return result;
    }
}

我们解析一下这段源码:

  1. 首先声明了一个 configurers 变量,用来保存所有的配置类,key 是配置类 Class 对象,值是一个 List 集合中放着配置类。

  2. apply 方法有两个,参数类型略有差异,主要功能基本一致,都是向 configurers 变量中添加配置类,具体的添加过程则是调用 add 方法。

  3. add 方法用来将所有的配置类保存到 configurers 中,在添加的过程中,如果 allowConfigurersOfSameType 变量为 true,则表示允许相同类型的配置类存在,也就是 List 集合中可以存在多个相同类型的配置类。默认情况下,如果是晋通配置类,allowConfigurersOfSameType 是 false,所以 List 集合中的配置类始终只有一个配置类;如果在 AuthenticationManagerBuilder 中设置 allowConfigurersOfSameType 为 true,此时相同类型的配置类可以有多个(下文会详细分析 AuthenticationManagerBuilder)。

  4. getConfigurers(Class<C>) 方法可以从 configurers 中返回某一个配置类对应的所有实例。

  5. removeConfigurers 方法可以从 configurers 中移除某一个配置类对应的所有实例,并返回被移除掉的配置类实例集合。

  6. getConfigurer 方法也是获取配置类实例,但是只获取集合中第一项。

  7. removeConfigurer 方法可以从 configurers 中移除某一个配置类对应的所有配置类实例,并返回被移除掉的配置类实例中的第一项。

  8. getConfigurers 方法是一个私有方法,主要是把所有的配置类实例放到一个集合中返回。在配置类初始化和配置的时候,会调用到该方法。

这些就是 AbstractConfiguredSecurityBuilder 中关于 configurers 的所有操作。

接下来就是 AbstractConfiguredSecurityBuilder 中的 doBuild 方法了,这是核心的构建方法,我们一起来看一下与之相关的方法:

@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;
    }
}

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);
    }
}
  1. 在 doBuild 方法中,一边更新构建状态,一边执行构建方法。构建方法中,beforeInit 是一个空的初始化方法,如果需要在初始化之前做一些准备工作,可以通过重写该方法实现。

  2. init 方法是所有配置类的初始化方法,在该方法中,遍历所有的配置类,并调用其 init 方法完成初始化操作。

  3. beforeConfigure 方法可以在 configure 方法执行之前做一些准备操作。该方法默认也是一个空方法。

  4. configure 方法用来完成所有配置类的配置,在 configure 方法中,遍历所有的配置类,分别调用其 configure 方法完成配置。

  5. performBuild 方法用来做最终的构建操作,前面的准备工作完成后,最后在 performBuild 方法中完成构建,这是一个抽象方法,具体的实现则在不同的配置类中。

这些就是 AbstractConfiguredSecurityBuilder 中最主要的几个方法,其他一些方法比较简单,这里就不一一赞述了。

ProviderManagerBuilder

ProviderManagerBuilder 继承自 SecurityBuilder 接口,并制定了构建的对象是 AuthenticationManager,代码如下:

public interface ProviderManagerBuilder<B extends ProviderManagerBuilder<B>> extends
		SecurityBuilder<AuthenticationManager> {

	B authenticationProvider(AuthenticationProvider authenticationProvider);
}

可以看到,ProviderManagerBuilder 中增加了一个 authenticationProvider 方法,同时通过泛型指定了构建的对象为AuthenticationManager。

AuthenticationManagerBuilder

AuthenticationManagerBuilder 用来构建 AuthenticationManager 对象,它继承自 AbstractConfiguredSecurityBuilder,并且实现了 ProviderManagerBuilder 接口,源码比较长,我们截取 部分常用代码,代码如下:

public class AuthenticationManagerBuilder
        extends
        AbstractConfiguredSecurityBuilder<AuthenticationManager, AuthenticationManagerBuilder>
        implements ProviderManagerBuilder<AuthenticationManagerBuilder> {

    public AuthenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
        super(objectPostProcessor, true);
    }

    public AuthenticationManagerBuilder parentAuthenticationManager(
            AuthenticationManager authenticationManager) {
        if (authenticationManager instanceof ProviderManager) {
            eraseCredentials(((ProviderManager) authenticationManager)
                    .isEraseCredentialsAfterAuthentication());
        }
        this.parentAuthenticationManager = authenticationManager;
        return this;
    }

    public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication()
            throws Exception {
        return apply(new InMemoryUserDetailsManagerConfigurer<>());
    }

    public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication()
            throws Exception {
        return apply(new JdbcUserDetailsManagerConfigurer<>());
    }

    public <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T> userDetailsService(
            T userDetailsService) throws Exception {
        this.defaultUserDetailsService = userDetailsService;
        return apply(new DaoAuthenticationConfigurer<>(
                userDetailsService));
    }

    public AuthenticationManagerBuilder authenticationProvider(
            AuthenticationProvider authenticationProvider) {
        this.authenticationProviders.add(authenticationProvider);
        return this;
    }

    @Override
    protected ProviderManager performBuild() throws Exception {
        if (!isConfigured()) {
            logger.debug("No authenticationProviders and no parentAuthenticationManager defined. Returning null.");
            return null;
        }
        ProviderManager providerManager = new ProviderManager(authenticationProviders,
                parentAuthenticationManager);
        if (eraseCredentials != null) {
            providerManager.setEraseCredentialsAfterAuthentication(eraseCredentials);
        }
        if (eventPublisher != null) {
            providerManager.setAuthenticationEventPublisher(eventPublisher);
        }
        providerManager = postProcess(providerManager);
        return providerManager;
    }
}
  1. 首先在 AuthenticationManagerBuilder 的构造方法中,调用了父类的构造方法,注意第二个参数传递了 true,表示允许相同类型的配置类同时存在(结合 AbstractConfiguredSecurityBuilder 的源码来理解)。

  2. parentAuthenticationManager 方法用来给一个 AuthenticationManager 设置 parent,

  3. inMemoryAuthentication 方法用来配置基于内存的数据源,该方法会自动创建 InMemoryUserDetailsManagerConfigurer 配置类,并最终将该配置类添加到父类的 configurers 变量中。由于设置了允许相同类型的配置类同时存在,因此 inMemoryAuthentication 方法可以反复调用多次。

  4. jdbcAuthentication 以及 userDetailsService 方法与 inMemoryAuthentication 方法类似,也是用来配置数据源的,这里不再资述。

  5. authenticationProvider 方法用来向 authenticationProviders 集合中添加 AuthenticationProvider 对象,根据前面第 3 章的介绍,我们已经知道一个 AuthenticationManager 实例中包含多个 AuthenticationProvider 实例,那么多个 AuthenticationProvider 实例可以通过 authenticationProvider 方法进行添加。

  6. performBuild 方法则执行具体的构建工作,常用的 AuthenticationManager 实例就是,ProviderManager,所以这里创建 ProviderManager 对象,并且配置 authenticationProviders 和  parentAuthenticationManager 对象,ProviderManager 对象创建成功之后,再去对象后置处理器中处理一遍再返回。

这就是 AuthenticationManagerBuilder 中的一个大致逻辑。

HttpSecurity

HttpSecurity 的主要作用是用来构建一条过滤器链,并反映到代码上,也就是构建一个 DefaultSecurityFilterChain 对象。一个 DefaultSecurityFilterChain 对象包含一个路径匹配器和多个 Spring Security 过滤器,HttpSecurity 中通过收集各种各样的 xxxConfigurer,将 Spring Security 过滤器对应的配置类收集起来,并保存到父类 AbstractConfiguredSecurityBuilder 的configurers 变量中,在后续的构建过程中,再将这些 xxxConfigurer 构建为具体的 Spring Security 过滤器,同时添加到 HttpSecurity 的 filters 对象中。

由于 HttpSecurity 中存在大量功能类似的方法,因此这里挑选一个作为例子用来说明 HttpSecurity 的配置原理,代码如下:

public final class HttpSecurity extends
		AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
		implements SecurityBuilder<DefaultSecurityFilterChain>,
		HttpSecurityBuilder<HttpSecurity> {

    public FormLoginConfigurer<HttpSecurity> formLogin() throws Exception {
		return getOrApply(new FormLoginConfigurer<>());
	}

	public HttpSecurity formLogin(Customizer<FormLoginConfigurer<HttpSecurity>> formLoginCustomizer) throws Exception {
		formLoginCustomizer.customize(getOrApply(new FormLoginConfigurer<>()));
		return HttpSecurity.this;
	}

	public HttpSecurity authenticationProvider(
			AuthenticationProvider authenticationProvider) {
		getAuthenticationRegistry().authenticationProvider(authenticationProvider);
		return this;
	}

	public HttpSecurity userDetailsService(UserDetailsService userDetailsService)
			throws Exception {
		getAuthenticationRegistry().userDetailsService(userDetailsService);
		return this;
	}

	private AuthenticationManagerBuilder getAuthenticationRegistry() {
		return getSharedObject(AuthenticationManagerBuilder.class);
	}

	@Override
	protected void beforeConfigure() throws Exception {
		setSharedObject(AuthenticationManager.class, getAuthenticationRegistry().build());
	}

	@Override
	protected DefaultSecurityFilterChain performBuild() {
		filters.sort(comparator);
		return new DefaultSecurityFilterChain(requestMatcher, filters);
	}

	public HttpSecurity addFilterAfter(Filter filter, Class<? extends Filter> afterFilter) {
		comparator.registerAfter(filter.getClass(), afterFilter);
		return addFilter(filter);
	}

	public HttpSecurity addFilterBefore(Filter filter,
			Class<? extends Filter> beforeFilter) {
		comparator.registerBefore(filter.getClass(), beforeFilter);
		return addFilter(filter);
	}

	public HttpSecurity addFilter(Filter filter) {
		Class<? extends Filter> filterClass = filter.getClass();
		if (!comparator.isRegistered(filterClass)) {
			throw new IllegalArgumentException(
					"The Filter class "
							+ filterClass.getName()
							+ " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead.");
		}
		this.filters.add(filter);
		return this;
	}

	public HttpSecurity addFilterAt(Filter filter, Class<? extends Filter> atFilter) {
		this.comparator.registerAt(filter.getClass(), atFilter);
		return addFilter(filter);
	}

	private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply(
			C configurer) throws Exception {
		C existingConfig = (C) getConfigurer(configurer.getClass());
		if (existingConfig != null) {
			return existingConfig;
		}
		return apply(configurer);
	}

}
  1. 以 form 表单登录配置为例,在 HttpSecurity 中有两个重载方法可以进行配置:第一个是一个无参的 formLogin 方法,该方法的返回值是一个 FormLoginConfigurer<HttpSecurity> 对象,开发者可以在该对象的基础续完善对 form 表单的配置,我在前面章节中配置的表单登录都是通过这种方式来进行配置的。第二个是一个有参的 formLogin 方法,该方法的参数是一个 FormLoginConfigurer 对象,返回值则是一个 HtpSecurity 对象,也就是说开发者可以提前在外面配置好 FormLoginConfigurer 对象,然后直接传进来进行配置即可,返回值 HttpSecurity 对象则可以在方法返回后直接进行其他过滤器的配置。无论是有参还是无参,最终都会调用到 getOrApply 方法,该方法会调用父类的 getConfigurer 方法去查看是否已经有对应的配置类了,如果有,则直接返回;如果没有,则调用 apply 方法添加到父类的 configurers 变量中。HttpSecurity 中其他过滤器的配置都和 form 表单登录配置类似,这里就不再赘述了。

  2. 每一套过滤器链都会有一个 AuthenticationManager 对象来进行认证操作(如果认证失败,则会调用 AuthenticationManager 的 parent 再次进行认证),主要是通过 authenticationProvider 方法配置执行认证的authenticationProvider 对象,通过 userDetailsService 方法配置 UserDetailsService,最后在 beforeConfigure 方法中触发AuthenticationManager 对象的构建。

  3. performBuild 方法则是进行 DefaultSecurityFilterChain 对象的构建,传入请求匹配器,和过滤器集合 filters,在构建之前,会先按照既定的顺序对 filters 进行排序。

  4. 通过 addFilterAfter、addFilterBefore 两个方法,我们可以在某一个过滤器之后或者之前添加一个自定义的过滤器(该方法己在 HttpSecurityBuilder 中声明,此处是具体实现)。

  5. addFilter 方法可以向过滤器链中添加一个过滤器,这个过滤器必须是 Spring Security 框架提供的过滤器的一个实例或者其扩展。实际上,在每一个 xxxConfigurer 的 configure 方法中,都会调用 addFilter 方法将构建好的过滤器添加到 HttpSecurity 中的 filters 集合中(addFilter 方法已在 HttpSecurityBuilder 中声明,此处是具体实现)。

  6. addFilterAt 方法可以在指定位置添加一人过滤器。需要注意的是,在同一个位置添加多个过滤器并不会覆盖现有的过滤器。

这便是 HttpSecurity 的基本功能。

WebSecurity

相比于 HttpSecurity,WebSecurity 是在一个更大的层面上去构建过滤器。一个 HttpSecurity 对象可以构建一个过滤器链,也就是一个 DefaultSecurityFilterChain 对象,而一个项目中可以存在多个 HttpSecurity 对象,也就可以构建多个 DefaultSecurityFilterChain 过滤器链。

WebSecurity 负责将 HttpSecurity 所构建的 DefaultSecurityFilterChain 对象(可能有多个),以及其他一些需要忽略的请求,再次重新构建为一个 FilterChainProxy 对象,同时添加上 HTTP 防火墙。

我们来看一下 WebSecurity 中的几个关键方法:

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

    private final List<RequestMatcher> ignoredRequests = new ArrayList<>();

    private final List<SecurityBuilder<? extends SecurityFilterChain>> securityFilterChainBuilders = new ArrayList<>();

    public WebSecurity httpFirewall(HttpFirewall httpFirewall) {
        this.httpFirewall = httpFirewall;
        return this;
    }

    public WebSecurity addSecurityFilterChainBuilder(
            SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder) {
        this.securityFilterChainBuilders.add(securityFilterChainBuilder);
        return this;
    }

    @Override
    protected Filter performBuild() throws Exception {
        Assert.state(
                !securityFilterChainBuilders.isEmpty(),
                () -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "
                        + "Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. "
                        + "More advanced users can invoke "
                        + WebSecurity.class.getSimpleName()
                        + ".addSecurityFilterChainBuilder directly");
        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;
        if (debugEnabled) {
            logger.warn("\n\n"
                    + "********************************************************************\n"
                    + "**********        Security debugging is enabled.       *************\n"
                    + "**********    This may include sensitive information.  *************\n"
                    + "**********      Do not use in a production system!     *************\n"
                    + "********************************************************************\n\n");
            result = new DebugFilter(filterChainProxy);
        }
        postBuildAction.run();
        return result;
    }
}
  1. 首先在 WebSecurity 中声明了 ignoredRequests 集合,这个集合中保存了所有被忽略的请求,因为在实际项目中,并非所有的请求都需要经过 Spring Security 过滤器链,有一些静态资源可能不需要权限认证,直接返回给客户端即可,那么这些需要忽略的请求可以直接保存在 ignoredRequests 变量中。

  2. 接下来声明了一个 securityFilterChainBuilders 集合,该集合用来保存所有的 HttpSecurity 对象,每一个 HttpSecurity 对象创建成功之后,通过 addSecurityFilterChainBuilder 方法将 HttpSecurity 对象添加到 securityFilterChainBuilders 集合中。

  3. httpFirewall 方法可以用来配置请求防火墙,关于请求防火墙,我们会在后面的章节中专门讲解。

  4. performBuild 方法则是具体的构建方法,在该方法中,首先统计出过滤器链的总个数(被忽略的请求个数+通过 HttpSecurity 创建出来的过滤器链个数),然后创建一个集合 securityFilterChains,遍历被忽略的请求并分别构建成 DefaultSecurityFilterChain 对象保存到 securityFilterChains 集合中。需要注意的是,对于被忽略的请求,在构建 DefaultSecurityFilterChain 对象时,只是传入了请求匹配器,而没有传入对应的过滤器链,这就意味看着这些被忽略掉的请求,将来不必经过 Spring Security 过滤器链;接下来再遍历 securityFilterChainBuilders 集合,调用每个对象的 build 方法构建 DefaultSecurityFilterChain 并存入 securityFilterChains 集合中,然后传入 SecurityFilterChains 集合构建 FilterChainProxy 对象,最后再设置 HTTP 防火墙。所有设置完成之后,最后返回 filterChainProxy 对象。

FilterChainProxy 就是我们最终构建出来的代理过滤器链,通过 Spring 提供的 DelegatingFilterProxy 将 FilterChainProxy 对象嵌入到 Web Filter 中(原生过滤器链中)。

读者可以回忆一下前面我们绘制的 FilterChainProxy 架构图,对照着来理解上面的源码应该就很容易了,如图4-3所示。

至此,关于 SecurityBuilder 体系中的几个关键类就介绍完了,至于 HttpSecurity 和 WebSecurity 是怎么配置到一起的,我们将在后面的章节中进行分析。

image 2024 04 12 09 58 42 384
Figure 3. 图 4-3 FilterChainProxy 架构图

FilterChainProxy

FilterChainProxy 通过 DelegatingFilterProxy 代理过滤器被集成到 Web Filter 中,DelegatingFilterProxy 作为一个代理对象,相信很多读者可能都用过(例如在 Spring 中整合 Shiro 就会用到),它不承载具体的业务。

所以,Spring Security 中的过滤器链的最终执行,就是在 FilterChainProxy 中,因此这里也来分析一下 FilterChainProxy 的源码。

FilterChainProxy 的源码比较长,我们一段一段来看:

private List<SecurityFilterChain> filterChains;

private FilterChainValidator filterChainValidator = new NullFilterChainValidator();

private HttpFirewall firewall = new StrictHttpFirewall();

public FilterChainProxy() {
}

public FilterChainProxy(SecurityFilterChain chain) {
    this(Arrays.asList(chain));
}

public FilterChainProxy(List<SecurityFilterChain> filterChains) {
    this.filterChains = filterChains;
}

首先声明了三个变量:

  1. 由于在 Spring Security 中可以同时存在多个过滤器链,filterChains 就是用来保存过滤器链的,注意保存的是过滤器链,而不是一个个具体的过滤器

  2. filterChainValidator 是一个过滤器链配置完成后的验证器,默认使用 NullFilterChainValidator 其实没有做任何验证。

  3. 创建了一个默认的防火墙对象 firewall。

在构造方法中传入过滤器链的集合,并赋值给 filterChains 变量。

由于 FilterChainProxy 本质上就是一个过滤器,因此它的核心方法就是 doFilter 方法,接下来我们来看一下 doFilter 方法:

@Override
public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain) throws IOException, ServletException {
    boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
    if (clearContext) {
        try {
            request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
            doFilterInternal(request, response, chain);
        }
        finally {
            SecurityContextHolder.clearContext();
            request.removeAttribute(FILTER_APPLIED);
        }
    }
    else {
        doFilterInternal(request, response, chain);
    }
}

doFilter 方法相当于是整个 Spring Security 过滤器链的入口,我们在前面章节中所涉及的一些具体的过滤器如 SecurityContextPersistenceFilter,都是在该 doFilter 方法之后执行的。作为整个过滤虑器链的入口,这里多了一个 clearContext 变量,如果是第一次执行该 doFilter 方法执行完成后,在 finally 代码块中需要从 SecurityContextHolder 里清除用户信息,这个主要是为了防止用户没有正确配置 SecurityContextPersistenceFilter,从而导致登录用户信息没有被正确清除,进而发生内存泄漏。

在 doFilter 方法中,过滤器的具体执行则交给了 doFilterInternal 方法:

private void doFilterInternal(ServletRequest request, ServletResponse response,
        FilterChain chain) throws IOException, ServletException {

    FirewalledRequest fwRequest = firewall
            .getFirewalledRequest((HttpServletRequest) request);
    HttpServletResponse fwResponse = firewall
            .getFirewalledResponse((HttpServletResponse) response);

    List<Filter> filters = getFilters(fwRequest);

    if (filters == null || filters.size() == 0) {
        if (logger.isDebugEnabled()) {
            logger.debug(UrlUtils.buildRequestUrl(fwRequest)
                    + (filters == null ? " has no matching filters"
                            : " has an empty filter list"));
        }

        fwRequest.reset();

        chain.doFilter(fwRequest, fwResponse);

        return;
    }

    VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
    vfc.doFilter(fwRequest, fwResponse);
}

private List<Filter> getFilters(HttpServletRequest request) {
    for (SecurityFilterChain chain : filterChains) {
        if (chain.matches(request)) {
            return chain.getFilters();
        }
    }

    return null;
}

在 doFilterInternal 方法中,首先会将 request 对象转换为一个 FirewalledRequest 对象,这个转换过程会进行 Http 防火墙处理(Http 防火墙将在第 8 章详细介绍),同时将 response 对象也转为 HttpServletResponse。接下来调用 getFilters 方法获取当前请求对应的过滤器链,getFilters 方法会遍历 filterChains 集合,进而判断出当前请求和哪一个过滤器链是对应的,如果找到的过滤器链 filters 为 null,或者 filters 中没有元素,说明当前请求并不需要经过 Spring Security 过滤器链,此时执行 fwRequest.reset 方法对 Http 防火墙中的属性进行重置,再执行 chain.doFilter 方法,回到 Web Filter 中,Spring Security 过滤器链将被跳过(回忆上一小结 WebSecurity 中配置的忽略请求)。如果 filters 集合中是有元素的,也就是说当前请求需要经过 filters 集合中元素所构成的过滤器链,那么构建一个虚拟的过滤器链对象 VirtualFilterChain 并执行其 doFilter 方法。

private static class VirtualFilterChain implements FilterChain {

    private final FilterChain originalChain;

    private final List<? extends Filter> additionalFilters;

    private int currentPosition = 0;

    public VirtualFilterChain(FilterChain chain, List<? extends Filter> additionalFilters) {
        this.originalChain = chain;
        this.additionalFilters = additionalFilters;
    }

    @Override
    public void doFilter(final ServletRequest request, final ServletResponse response)
            throws IOException, ServletException {

        if (this.currentPosition == this.additionalFilters.size()) {
            this.originalChain.doFilter(request, response);
        }
        else {
            this.currentPosition++;
            Filter nextFilter = this.additionalFilters.get(this.currentPosition - 1);
            nextFilter.doFilter(request, response, this);
        }
    }
}

VirtualFilterChain 中首先声明了五个变量:

  1. originalChain:表示原生的过滤器链,执行它的 doFilter 方法会回到 Web Filter 中。

  2. additionalFilters:这个 List 集合中存储诸的 Filter 就是本次请求的 Filter。

  3. firewalledRequest:当前请求对象。

  4. size:过滤器链的大小。

  5. currentPosition:过滤器链执行的下标。

在 VirtualFilterChain 的构造方法中,会给相应的变量赋值。

在 doFilter 方法中,会首先判断当前执行的下标是否等于过滤器链的大小,如果相等,则说明整个过滤器链中的所有过滤器都已经挨个走一遍了,此时先对 Http 防火墙中的属性进行重置,然后调用 originalChain.doFilter 方法跳出 Spring Security Filter,回到 Web Filter;如果不相等,则 currentPosition 自增,然后从过滤器链集合中取出一个过滤器去执行,注意执行的时候第三个参数 this 表示当前对象(即 VirtualFilterChain),这样在每一个过滤虑器执行完之后,最后的 chain.doFilter 方法又会回到当前 doFilter 方法中,继续下一个过滤器的调用。

这就是 FilterChainProxy 的一个大致工作原理。

SecurityConfigurer

SecurityConfigurer 中有两个核心方法,一个是 init 方法,用来完成配置类的初始化操作,另外一个是 configure 方法,进行配置类的配置。上一小结介绍的 AbstractConfiguredSecurityBuilder 类,里边的 init方法和 configure 其实就是在遍历执行不同配置类的 init 和 configure 方法。

SecurityConfigurer 的实现类比较多,这里主要梳理一下常见的 SecurityConfigurer 实现类我们分别来看一下。

SecurityConfigurer

先来看 SecurityConfigurer 源码,代码如下:

public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {

	void init(B builder) throws Exception;

	void configure(B builder) throws Exception;
}

可以看到,SecurityConfigurer 只有两个方法:init 和 configure,两个方法的参数都是 SecurityBuilder 对象,也就是说在这两个方法中对 SecurityBuilder 进行初始化和配置。

SecurityConfigurer 的子类非常多,因为每一个过滤器都有自己对应的 xxxConfigurer,这里着重介绍几个关键的实现类,如图4-4所示。

image 2024 04 12 10 22 15 320
Figure 4. 图4-4 SecurityConfigurer 的子类

我们分别来看这几个实现类。

SecurityConfigurerAdapter

SecurityConfigurerAdapter 实现了 SecurityConfigurer 接,它的源码如下:

public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>>
        implements SecurityConfigurer<O, B> {
    private B securityBuilder;

    private CompositeObjectPostProcessor objectPostProcessor = new CompositeObjectPostProcessor();

    public void init(B builder) throws Exception {
    }

    public void configure(B builder) throws Exception {
    }

    public B and() {
        return getBuilder();
    }

    protected final B getBuilder() {
        if (securityBuilder == null) {
            throw new IllegalStateException("securityBuilder cannot be null");
        }
        return securityBuilder;
    }

    @SuppressWarnings("unchecked")
    protected <T> T postProcess(T object) {
        return (T) this.objectPostProcessor.postProcess(object);
    }

    public void addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
        this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);
    }

    public void setBuilder(B builder) {
        this.securityBuilder = builder;
    }

    private static final class CompositeObjectPostProcessor implements
            ObjectPostProcessor<Object> {
        private List<ObjectPostProcessor<?>> postProcessors = new ArrayList<>();

        @SuppressWarnings({ "rawtypes", "unchecked" })
        public Object postProcess(Object object) {
            for (ObjectPostProcessor opp : 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;
        }

        private boolean addObjectPostProcessor(
                ObjectPostProcessor<?> objectPostProcessor) {
            boolean result = this.postProcessors.add(objectPostProcessor);
            postProcessors.sort(AnnotationAwareOrderComparator.INSTANCE);
            return result;
        }
    }
}

从这段源码中,我们可以分析出 SecurityConfigurerAdapter 主要做了如下几件事:

  1. 提供了一个 SecurityBuilder 对象,为每一个配置类都提供一个 SecurityBuilder 对象,将来通过 SecurityBuilder 构建出具体的配置对象;通过 and 方法返回 SecurityBuilder 对象,这样方便不同的配置类在配置时,可以进行链式配置(第 2 章中我们在定义 SecurityConfig 时所使用的 and 方法)。

  2. 定义了内部类 CompositeObjectPostProcessor,这是一个复合的对象后置处理器。

  3. 提供了一个 addObjectPostProcessor 方法,通过该方法可以向复合的对象后置处理器中添加新的 objectPostProcessor 实例。

这是 SecurityConfigurerAdapter 提供的主要功能。

UserDetailsAwareConfigurer

UserDetailsAwareConfigurer 的子类主要负责配置用户认证相关的组件,如 UserDetailsService 等,UserDetailsAwareConfigurer 中提供了获取 UserDetailsService 的抽象方法,具体实现则在它的子类中,UserDetailsAwareConfigurer 的子类如图4-5 所示。

image 2024 04 12 10 27 38 805
Figure 5. 图 4-5 UserDetailsAwareConfigurer 的子类
  • AbstractDaoAuthenticationConfigurer:完成对 DaoAuthenticationProvider 的配置。

  • UserDetailsServiceConfigurer:完成对 UserDetailsService 的配置。

  • UserDetailsManagerConfigurer :使用 UserDetailsManager 构建用户对象,完成对 AuthenticationManagerBuilder 的填充。

  • JdbcUserDetailsManagerConfigurer: 配置 JdbcUserDetailsManager 并填充到 AuthenticationManagerBuilder 中。

  • InMemoryUserDetailsManagerConfigurer: 配置 InMemoryUserDetailsManager。

  • DaoAuthenticationConfigurer:完成对 DaoAuthenticationProvider 的配置。

AbstractHttpConfigurer

AbstractHttpConfigurer 主要是为了给在 HttpSecurity 中使用的配置类添加一个方便的父类,提取出共同的操作。

public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>, B extends HttpSecurityBuilder<B>>
		extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {

	@SuppressWarnings("unchecked")
	public B disable() {
		getBuilder().removeConfigurer(getClass());
		return getBuilder();
	}

	@SuppressWarnings("unchecked")
	public T withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
		addObjectPostProcessor(objectPostProcessor);
		return (T) this;
	}
}

可以看到,提取出来的方法其实就两个:一个 disable 表示禁用某一个配置(第 2 章中我们配置的 .csrf().disable()),本质上就是从构建器的 configurers 集合中移除某一个配置类,这样在将来构建的时候就不存在该配置类,那么对应的功能也就不存在(被禁用);另一个 withObjectPostProcessor 表示给某一个对象添加一个对象后置处理器,由于该方法的返回值是当前对象,所以该方法可以用在链式配置中。

AbstractHttpConfigurer 的实现类比较多,基本上都用来配置各种各样的过滤器,参见表 4-1。

Table 1. 表4-1ad AbstractHttpConfigurer子类及其作用
配置类名称 作用

HttpBasicConfigurer

配置基于 Http Basic 认证的过滤器 BasicAuthenticationFilter

LogoutConfigurer

配置注销登录过滤器 LogoutFilter

RequestCacheConfigurer

配置请求缓存过滤器 RequestCacheAwareFilter

RememberMeConfigurer

配置记住我登录过滤器 RememberMeAuthenticationFilter

ServletApiConfigurer

配置包装原始请求过滤器 SecurityContextHolderAwareRequestFilter

DefaultLoginPageConfigurer

配置提供默认登录页面的过滤器 DefaultLoginPageGeneratingFilter 和默认注销页面的过滤器 DefaultLogoutPageGeneratingFilter

SessionManagementConfigurer

配置 Session 管理过滤器 SessionManagementFilter 和 ConcurrentSessionFilter

PortMapperConfigurer

配置一个共享的 PortMapper 实例,以便在 HTTP 和 HTTPS 之间重定向时确定端口

ExceptionHandlingConfigurer

配置异常处理过滤器 ExceptionTranslationFilter

HeadersConfigurer

配置安全相关的响应头信息

CsrfConfigurer

配置防范 CSRF攻击过滤器 CsrfFilter

OAuth2ClientConfigurer

配置 OAuth2 相关的过滤器 OAuth2AuthorizationRequestRedirectFilter 和 OAuth2AuthorizationCodeGrantFilter

ImplicitGrantConfigurer

配置 OAuth2 认证请求重定向的过滤器 OAuth2AuthorizationRequestRedirectFilter

AnonymousConfigurer

配置匿名过滤器 AnonymousAuthenticationFilter

JeeConfigurer

配置 J2EE 身份预校验过滤器 J2eePreAuthenticatedProcessingFilter

ChannelSecurityConfigurer

配置请求协议处理过滤器 ChannelProcessingFilter

CorsConfigurer

配置处理跨域过滤器CorsFilter

SecurityContextConfigurer

配置登录信息存储和恢复的过滤器SecurityContextPersistenceFilter

OAuth2ResourceServerConfigurer

配置 OAuth2 身份请求认证过滤器 BearerTokenAuthenticationFilter

AbstractAuthenticationFilterConfigurer

身份认证配置类的父类

FormLoginConfigurer

配置身份认证过滤器 UsernamePasswordAuthenticationFilter 和默认登录页面的过滤器 DefaultLoginPageGeneratingFilter

OAuth2LoginConfigurer

配置 OAuth2 认证请求重定向的过滤器 OAuth2AuthorizationRequestRedirectFilter 和处理第三方回调过滤器 OAuth2LoginAuthenticationFilter

OpenIDLoginConfigurer

配置 OpenID 身份认证过滤器 OpenDAuthenticationFilter

Saml2LoginConfigurer

配置 SAML2.0 身份认证过滤器 Saml2WebSsoAuthenticationFilter 和 Saml2WebSsoAuthenticationRequestFilter

X509Configurer

配置 X509 身份认证过滤器 X509AuthenticationFilter

AbstractInterceptUrlConfigurer

拦截器配置类的父类

UrlAuthorizationConfigurer

配置基于URL的权限认证拦截器 FilterSecurityInterceptor

ExpressionUrlAuthorizationConfigurer

配置基于 SpEL 表达式的 URL 权限认证拦截器 FilterSecurityInterceptor

GlobalAuthenticationConfigurerAdapter

GlobalAuthenticationConfigurerAdapter 主要用于配置全局 AuthenticationManagerBuilder, 在 AuthenticationConfiguration 类中会自动使用 GlobalAuthenticationConfigurerAdapter 提供的 Bean 来配置全局 AuthenticationManagerBuilder。

在第 3 章介绍 ProviderManager 时曾经提到过,默认情况下 ProviderManager 有一个 parent,这个 parent 就是通过这里的全局 AuthenticationManagerBuilder 来构建的。

GlobalAuthenticationConfigurerAdapter 有四个不同的子类,如图 4-6 所示。

image 2024 04 12 12 09 17 521
Figure 6. 图4-6 GlobalAuthenticationConfigurerAdapter 的子类
  • InitializeAuthenticationProviderBeanManagerConfigurer :初始化全局的 Authentication Provider 对象。

  • InitializeAuthenticationProviderManagerConfigurer:配置全局的 AuthenticationProvider 对象,配置过程就是从 Spring 容器中查找 AuthenticationProvider 并设置给全局的 AuthenticationManagerBuilder 对象。

  • InitializeUserDetailsBeanManagerConfigurer:初始化全局的 UserDetailsService 对象。

  • InitializeUserDetailsManagerConfigurer:配置全局的 UserDetailsService 对象,配置过程就是从 Spring 容器中查找 UserDetailsService,并设置给全局的 AuthenticationManagerBuilder 对象。

  • EnableGlobalAuthenticationAutowiredConfigurer: 从 Spring 容器中加载被 @EnableGlobal Authentication 注解标记的 Bean。

WebSecurityConfigurer

WebSecurityConfigurer 是一个空接口,我们可以通过它来自定义 WebSecurity。WebSecurityConfigurer 只有一个实现类就是 WebSecurityConfigurerAdapter,在大多数情况下,开发者通过继承WebSecurityConfigurerAdapter 来实现对 WebSecurity 的自定义配置。

WebSecurityConfigurerAdapter

WebSecurityConfigurerAdapter 是一个可以方便创建 WebSecurityConfigurer 实例的基类,开发者可以通过覆盖 WebSecurityConfigurerAdapter 中的方法完成对 HttpSecurity 和 WebSecurity 的定制。在本书前面的章节中,我们所定制的 Spring Security 登录都是通过自定义类继承 WebSecurityConfigurerAdapter 来实现的。

在 WebSecurityConfigurerAdapter 中声明了两个 AuthenticationManagerBuilder 对象用来构建 AuthenticationManager:

private AuthenticationManagerBuilder authenticationBuilder;
private AuthenticationManagerBuilder localConfigureAuthenticationBldr;

其中,localConfigureAuthenticationBldr 对象负责构建全局的AuthenticationManager,而 authenticationBuilder 则负责构建局部的 AuthenticationManager。局部的 AuthenticationManager 是和每一个 HttpSecurity 对象绑定的,而全局的 AuthenticationManager 对象则是所有局部 AuthenticationManager 的 parent。需要注意的是,localConfigureAuthenticationBldr 并非总是有用,在开发者没有重写 configure(AuthenticationManagerBuilder) 方法的情况下,全局的 AuthenticationManager 对象是由 AuthenticationConfiguration 类中的 getAuthenticationManager 方法提供的,如果用户重写了 configure(AuthenticationManagerBuilder) 方法,则全局的 AuthenticationManager 就由 localConfigureAuthenticationBldr 负责构建。这里可能会感觉有点 绕,在后面的小节中,我们将通过实际的例子展示全局 AuthenticationManager 对象的构建。

WebSecurityConfigurerAdapter 类的初始化方法如下:

public void init(final WebSecurity web) throws Exception {
    final HttpSecurity http = getHttp();
    web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
        FilterSecurityInterceptor securityInterceptor = http
                .getSharedObject(FilterSecurityInterceptor.class);
        web.securityInterceptor(securityInterceptor);
    });
}

protected final HttpSecurity getHttp() throws Exception {
    if (http != null) {
        return http;
    }

    AuthenticationEventPublisher eventPublisher = getAuthenticationEventPublisher();
    localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);

    AuthenticationManager authenticationManager = authenticationManager();
    authenticationBuilder.parentAuthenticationManager(authenticationManager);
    Map<Class<?>, Object> sharedObjects = createSharedObjects();

    http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
            sharedObjects);
    if (!disableDefaults) {
        // @formatter:off
        http
            .csrf().and()
            .addFilter(new WebAsyncManagerIntegrationFilter())
            .exceptionHandling().and()
            .headers().and()
            .sessionManagement().and()
            .securityContext().and()
            .requestCache().and()
            .anonymous().and()
            .servletApi().and()
            .apply(new DefaultLoginPageConfigurer<>()).and()
            .logout();
        // @formatter:on
        ClassLoader classLoader = this.context.getClassLoader();
        List<AbstractHttpConfigurer> defaultHttpConfigurers =
                SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);

        for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
            http.apply(configurer);
        }
    }
    configure(http);
    return http;
}

protected void configure(HttpSecurity http) throws Exception {
    logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");

    http
        .authorizeRequests()
            .anyRequest().authenticated()
            .and()
        .formLogin().and()
        .httpBasic();
}

protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    this.disableLocalConfigureAuthenticationBldr = true;
}

protected AuthenticationManager authenticationManager() throws Exception {
    if (!authenticationManagerInitialized) {
        configure(localConfigureAuthenticationBldr);
        if (disableLocalConfigureAuthenticationBldr) {
            authenticationManager = authenticationConfiguration
                    .getAuthenticationManager();
        }
        else {
            authenticationManager = localConfigureAuthenticationBldr.build();
        }
        authenticationManagerInitialized = true;
    }
    return authenticationManager;
}
  1. 在 init 方法中,首先调用 getHttp 方法获取一个HttpSecurity 实例,并将获取到的实例添加到 WebSecurity 对象中,再由 WebSecurity 对象进行构建。

  2. 在 getHttp 方法中,如果 http 对象已经初始化,则直接返回,否则进行初始化操作。在初始化的过程中,给 localConfigureAuthenticationBldr 设置事件发布器,并调用 authenticationManager 方法获取全局的 AuthenticationManager 对象。

  3. 在 authenticationManager方法中,如果全局的 AuthenticationManager 对象还没有初始化,则先调用 configure 方法,该方法的逻辑很简单,就是将 disableLocalConfigureAuthenticationBldr 变量由 false 变为 true,接下来就会进入到 authenticationManager 方法的 if 分支中,通过调用 authenticationConfiguration.getAuthenticationManager() 方法获取全局的 AuthenticationManager 对象并返回。如果开发者自己重写了configure(AuthenticationManagerBuilder) 方法,则 disableLocalConfigureAuthenticationBldr 变量就一直是 false,没有机会变为 true,这样就会进入到 else 分支中,通过 localConfigureAuthenticationBldr 变量来构建 authenticationManager 对象。

  4. 再次回到 getHttp 方法中,获取到全局的 authenticationManager 对象之后,设置给 authenticationBuilder,然后创建一个 HttpSecurity 实例出来,并为其配置上默认的过滤器。默认的配置完成后,调用 configure(HttpSecurity) 方法进行扩展配置,WebSecurityConfigurerAdapter 中对 configure(HtpSecurity) 方法提供了默认的实现,开发者也可以自定义该方法。

这就是 WebSecurityConfigurerAdapter 的初始化方法,其实就是创建并配置一个 HttpSecurity 实例,之后添加到 WebSecurity 中。

WebSecurityConfigurerAdapter 中的 configure 方法是一个空方法,可以用来配置 WebSecurity,代码如下:

public void configure(WebSecurity web) throws Exception {

}

一般来说,如果我们有一些静态资源不需要经过 Spring Security 过滤器,就可以通过重写该方法实现。

至此,在 Spring Security 初始化过程中,几个重要的组件都介绍完了,单纯的源码读者看起来可能会比较枯燥,在后面的小节中,我们会结合大量的应用案例,来帮助大家深入理解源码。

不过在讲解具体的案例之前,我们还是先来分析一遍 Spring Security 的初始化流程,将前面讲的这些知识点串起来。

初始化流程分析

在 Spring Boot 中使用 Spring Security,初始化就从 Spring Security 的自动化配置类中开始:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class)
@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
		SecurityDataConfiguration.class })
public class SecurityAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
	public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
		return new DefaultAuthenticationEventPublisher(publisher);
	}

}

可以看到,在自动化配置类 SecurityAutoConfiguration 中,最重要的就是导入了三个配置类,并且定义了一个默认的事件发布器。

导入的三个配置类中,SpringBootWebSecurityConfiguration 的主要作用是在开发者没有提供 WebSecurityConfigurerAdapter。实例的情况下,由其负责提供一个默认的 WebSecurityConfigurerAdapter 实例, 代码如下:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
public class SpringBootWebSecurityConfiguration {

	@Configuration(proxyBeanMethods = false)
	@Order(SecurityProperties.BASIC_AUTH_ORDER)
	static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {

	}

}

另一个导入的配置类 SecurityDataConfiguration 主要提供了一个 SecurityEvaluationContextExtension 实例,以便通过 SpEL 为经过身份验证的用户提供数据查询:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(SecurityEvaluationContextExtension.class)
public class SecurityDataConfiguration {

	@Bean
	@ConditionalOnMissingBean
	public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
		return new SecurityEvaluationContextExtension();
	}

}

最后一个导入的配置类 WebSecurityEnablerConfiguration 则是我们分析的重点。

@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@EnableWebSecurity
public class WebSecurityEnablerConfiguration {

}

WebSecurityEnablerConfiguration 配置类中添加了 @EnableWebSecurity 注解,而该注解的定义,引入了关键的配置类 WebSecurityConfiguration。

@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class,
		SpringWebMvcImportSelector.class,
		OAuth2ImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {

	boolean debug() default false;
}

可以看到,@EnableWebSecurity 是一个组合注解,首先导入了三个配置类:

  • WebSecurityConfiguration:用来配置 WebSecurity(重点分析)。

  • SpringWebMvcImportSelector:判断当前环境是否存在 Spring MVC,如果存在,则引入相关配置。

  • OAuth2ImportSelector:判断当前环境是否存在 OAuth2,如果存在,则引入相关配置。

另外还有一个 @EnableGlobalAuthentication 注解,用来开启全局配置,代码如下:

@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import(AuthenticationConfiguration.class)
@Configuration
public @interface EnableGlobalAuthentication {
}

可以看到,@EnableGlobalAuthentication 注解的主要功能是导入了配置类 AuthenticationConfiguration。

从上面的源码中我们可以看到,Spring Security 的自动化配置类主要导入了两个类:WebSecurityConfiguration 和 AuthenticationConfiguration。接下来我们就来分析这两个类。

WebSecurityConfiguration

WebSecurityConfiguration 配置类的功能,主要就是为了构建 Spring Security 过滤器链代理对象 FilterChainProxy。根据前面的分析,FilterChainProxy 是由 WebSecurity 来构建的,所以在 WebSecurityConfiguration 中会首先构建 WebSecurity 对象,再利用 WebSecurity 对象构建出 FilterChainProxy。

我们先来看一下 WebSecurityConfiguration 中定义的属性:

@Configuration(proxyBeanMethods = false)
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
	private WebSecurity webSecurity;

	private Boolean debugEnabled;

	private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;

	private ClassLoader beanClassLoader;

	@Autowired(required = false)
	private ObjectPostProcessor<Object> objectObjectPostProcessor;
}
  1. WebSecurityConfiguration 类实现了 ImportAware 接口。ImportAware 接‧一般是和 @Import 注解一起使用,实现了 ImportAware 接口的配置类可以方便地通过 setImportMetadata 方法获取到导入类中的数据配置。换句话说,WebSecurityConfiguration 实现了 ImportAware 接口,使用 @Import 注解在 @EnableWebSecurity 上导入 WebSecurityConfiguration 之后,在 WebSecurityConfiguration 的 setImportMetadata 方法中可以方便的获取到 @EnableWebSecurity 注解中的属性值,这里主要是 debug 属性。另一方面,WebSecurityConfiguration 类通过实现 BeanClassLoaderAware 接口可以方便地获取到 ClassLoader 对象。

  2. webSecurity 对象是 WebSecurityConfiguration 中需要构建的 WebSecurity 对象。

  3. webSecurityConfigurers 集合中保存了所有的配置类,也就是 WebSecurityConfigurerAdapter 对象,一个 WebSecurityConfigurerAdapter 对象可以创建一个 HttpSecurity,进而构建出一条过滤器链,多个 WebSecurityConfigurerAdapter 对象就可以构建出多条过滤器链。

  4. beanClassLoader 是一个 ClassLoader。

  5. objectObjectPostProcessor 是一个对象后置处理器,注意这个对象是直接从 Spring 容器中注入的。下一小节会分析对象后置处理器是什么时候初始化并注册到 Spring 容器中去的。

这是 WebSecurityConfiguration 类中定义的属性。接下来,我们来看一下 setFilterChainProxySecurityConfigurer 方法,该方法主要用来构建一个 WebSecurity 对象,并且加载所有的配置类对象。

@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
        ObjectPostProcessor<Object> objectPostProcessor,
        @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
        throws Exception {
    webSecurity = objectPostProcessor
            .postProcess(new WebSecurity(objectPostProcessor));
    if (debugEnabled != null) {
        webSecurity.debug(debugEnabled);
    }

    webSecurityConfigurers.sort(AnnotationAwareOrderComparator.INSTANCE);

    Integer previousOrder = null;
    Object previousConfig = null;
    for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
        Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
        if (previousOrder != null && previousOrder.equals(order)) {
            throw new IllegalStateException(
                    "@Order on WebSecurityConfigurers must be unique. Order of "
                            + order + " was already used on " + previousConfig + ", so it cannot be used on "
                            + config + " too.");
        }
        previousOrder = order;
        previousConfig = config;
    }
    for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
        webSecurity.apply(webSecurityConfigurer);
    }
    this.webSecurityConfigurers = webSecurityConfigurers;
}

@Bean
public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
        ConfigurableListableBeanFactory beanFactory) {
    return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
}

setFilterChainProxySecurityConfigurer 方法有两个参数,第一个参数 objectPostProcessor 是一个对象后置处理器,由于该方法有一个 @Autowired 注解,会自动查找需要注入的参数,所以 objectPostProcessor 参数会自动注入进来。需要注意的是,@Autowired 注解的 required 属性为 false,所以在方法参数注入的时候,有就注入,没有则忽略。required 属性设置为 false 主要是针对第二个参数 webSecurityConfigurers,因为该参数的值是通过调用 autowiredWebSecurityConfigurersIgnoreParents 对象的 getWebSecurityConfigurers 方法获取的。autowiredWebSecurityConfigurersIgnoreParents 对象也是在当前类中注入到 Spring 容器中的,我们来看一下它的 getWebSecurityConfigurers 方法:

public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
    List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<>();
    Map<String, WebSecurityConfigurer> beansOfType = beanFactory
            .getBeansOfType(WebSecurityConfigurer.class);
    for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
        webSecurityConfigurers.add(entry.getValue());
    }
    return webSecurityConfigurers;
}

可以看到,在 getWebSecurityConfigurers 方法中主要是通过调用 beanFactory.getBeansOfType 方法来获取 Spring 容器中所有的 WebSecurityConfigurer 实例,也就是开发者自定义的各种各样继承自 WebSecurityConfigurerAdapter 的配置类。如果开发者没有自定义任何配置类,那么这里获取到的就是前面所讲的 SpringBootWebSecurityConfiguration 类中提供的默认配置类,将获取到的所有配置类实例放入 webSecurityConfigurers 集合中并返回。

返回 setFilterChainProxySecurityConfigurer 方法中,现在我们已经明白了第二个参数 webSecurityConfigurers 的含义了。在该方法中,首先创建一个 WebSecurity 实例,创建出来之后去对象后置处理器中走一圈,这样就将 webSecurity 对象注册到 Spring 容器中了。接下来,根据每一个配置类的 @Order 注解对 webSecurityConfigurers 集合中的所有配置类进行排序,因为一个配置类对应一个过滤器链,当请求到来后,需要先和哪个过滤器链进行匹配,这里必然存在一个优先级问题,所以如果开发者自定义了多个配置类,则需要通过 @Order 注解标记多个配置类的优先级。排序完成后,进人到循环,检查是否存在优先级相等的配置类,如果存在,则直接抛出异常。最后再去遍历所有的配置类,调用 webSecurity.apply 方法将其添加到 webSecurity 父类中的 configurers 集合中(将来遍历该集合并分别调用配置类的 init 和 configure 方法完成配置类的初始化操作)。

这是 setFilterChainProxySecurityConfigurer 方法的执行逻辑,该方法主要用来初始化 WebSecurity 对象,同时收集到所有的自定义配置类。

有了 WebSecurity 对象和配置类,接下来就可以构建过滤器 FilterChainProxy 了。我们来看一下 springSecurityFilterChain 方法:

@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
    boolean hasConfigurers = webSecurityConfigurers != null
            && !webSecurityConfigurers.isEmpty();
    if (!hasConfigurers) {
        WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
                .postProcess(new WebSecurityConfigurerAdapter() {
                });
        webSecurity.apply(adapter);
    }
    return webSecurity.build();
}

这里首先判断 webSecurityConfigurers 集合中是否存在配置类,如果不存在,则立马创建一个匿名的 WebSecurityConfigurerAdapter 对象并注册到 Spring 容器中,否则就直接调用 WebSecurity 的 build 方法进行构建。

根据前面小节的介绍,了解了 WebSecurity 对象的 build 方法执行后,首先会对所有的配置类即 WebSecurityConfigurerAdapter 实例进行构建,在 WebSecurityConfigurerAdapter 的 init 方法中,又会完成 HttpSecurity 的构建,而 HttpSecurity 的构建过程中,则会完成局部 AuthenticationManager 对象以及每一个具体的过滤器的构建。

这就是整个过滤器链的构建流程。

AuthenticationConfiguration

在 Spring Security 自动化配置类中导入的另外一个配置类是 AuthenticationConfiguration,该类的功能主要是做全局的配置,同时提供一个全局的 AuthenticationManager 实例。首先我们来看 AuthenticationConfiguration 类的定义:

@Configuration(proxyBeanMethods = false)
@Import(ObjectPostProcessorConfiguration.class)
public class AuthenticationConfiguration {}

可以看到,AuthenticationConfiguration 类的定义中,导入了 ObjectPostProcessorConfiguration 配置,而 ObjectPostProcessorConfiguration 配置则提供了一个基本的对象后置处理器:

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ObjectPostProcessorConfiguration {

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public ObjectPostProcessor<Object> objectPostProcessor(
			AutowireCapableBeanFactory beanFactory) {
		return new AutowireBeanFactoryObjectPostProcessor(beanFactory);
	}
}

可以看到,ObjectPostProcessorConfiguration 类主要提供了一个 ObjectPostProcessor 实例,具体的实现类是 AutowireBeanFactoryObjectPostProcessor,根据 4.1.1 小节的介绍,该实现类主要用来将一个对象注册到 Spring 容器中去,我们在其他配置类中所见到的 ObjectPostProcessor 实例其实都是这里提供的。

这是 AuthenticationConfiguration 类的定义部分,AuthenticationConfiguration 类中的方法比较多,我们挑选出关键的部分分析一下:

@Bean
public AuthenticationManagerBuilder authenticationManagerBuilder(
        ObjectPostProcessor<Object> objectPostProcessor, ApplicationContext context) {
    LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);
    AuthenticationEventPublisher authenticationEventPublisher = getBeanOrNull(context, AuthenticationEventPublisher.class);

    DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, defaultPasswordEncoder);
    if (authenticationEventPublisher != null) {
        result.authenticationEventPublisher(authenticationEventPublisher);
    }
    return result;
}

@Bean
public static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(
        ApplicationContext context) {
    return new EnableGlobalAuthenticationAutowiredConfigurer(context);
}

@Bean
public static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(ApplicationContext context) {
    return new InitializeUserDetailsBeanManagerConfigurer(context);
}

@Bean
public static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(ApplicationContext context) {
    return new InitializeAuthenticationProviderBeanManagerConfigurer(context);
}

public AuthenticationManager getAuthenticationManager() throws Exception {
    if (this.authenticationManagerInitialized) {
        return this.authenticationManager;
    }
    AuthenticationManagerBuilder authBuilder = this.applicationContext.getBean(AuthenticationManagerBuilder.class);
    if (this.buildingAuthenticationManager.getAndSet(true)) {
        return new AuthenticationManagerDelegator(authBuilder);
    }

    for (GlobalAuthenticationConfigurerAdapter config : globalAuthConfigurers) {
        authBuilder.apply(config);
    }

    authenticationManager = authBuilder.build();

    if (authenticationManager == null) {
        authenticationManager = getAuthenticationManagerBean();
    }

    this.authenticationManagerInitialized = true;
    return authenticationManager;
}
  1. 首先定义了一个 AuthenticationManagerBuilder 实例,目的是为了构建全局的 AuthenticationManager 对象,这个过程中会从 Spring 容器中查找 AuthenticationEventPublisher 实例设置给 AuthenticationManagerBuilder 对象。

  2. 接下来构建了三个 Bean,这三个 Bean 的作用在 4.1.5 小节中已经介绍过了,这里就不再资述了。

  3. getAuthenticationManager 方法则用来构建具体的 AuthenticationManager 对象,在该方法内部,会首先判断 AuthenticationManager 对象是否已经初始化,如果已经初始化,则直接返回 AuthenticationManager 对象,否则就先从 Spring 容器中获取到 AuthenticationManagerBuilder 对象。注意这里还多了一个 AuthenticationManagerDelegator 对象,这个主要是为了防止在初始化 AuthenticationManager 时进行无限递归。拿到 authBuilder 对象之后,接下来遍历 globalAuthConfigurers 配置类集合(也就是第二点中所说的三个配置类),将配置类分别添加到 authBuilder 对象中,然后进行构建,最终将构建结果返回。

这是全局 AuthenticationManager 的构建过程。

整体来说,AuthenticationConfiguration 的作用主要体现在两方面:第一就是导入了 ObjectPostProcessorConfiguration 配置类;第二则是提供了一个全局的 AuthenticationManager 对象。

如果开发者在自定义配置类中重写了 configure(AuthenticationManagerBuilder) 方法,这里的全局 AuthenticationManager 对象将不会生效,而大部分情况下,开发者都会重写 configure(AuthenticationManagerBuilder) 方法。

至此,Spring Security 初始化就讲解完了。然而这里的架构复杂,概念繁多,可能有读者看完之后还是理解不到位,因此,接下来我们将通过几个不同的案例,展示前面这些组件的不同用法,加深大家对 Spring Security 基础组件的理解。