SpringSecurity - 启动流程分析(一)

概述

在 SpringBoot - 简单集成 SpringSecurity 这篇文章中,我们知道只要在配置类上加上 @EnableWebSecurity 注解,就可以使我们的应用有一个默认配置的安全校验。接下我们就以 @EnableWebSecurity 为入口跟踪一下 Spring 在底层为我们做了哪些工作。

@EnableWebSecurity

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class, HttpSecurityConfiguration.class})
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
    boolean debug() default false;
}

@EnableWebSecurity 使我们拥有了 SpringSecurity 的能力,并为 IoC 容器 中导入了几个配置类,比较重要的一个配置是:WebSecurityConfiguration,我们来看一下 WebSecurityConfiguration 为我们做了哪些工作

WebSecurityConfiguration

这里忽略了一些非主流程的代码,我们只需要关心重要流程

@Configuration(proxyBeanMethods = false)
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {

	// 在 setFilterChainProxySecurityConfigurer 方法中初始化
	private WebSecurity webSecurity;

	private Boolean debugEnabled;

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

	private List<SecurityFilterChain> securityFilterChains = Collections.emptyList();

	private List<WebSecurityCustomizer> webSecurityCustomizers = Collections.emptyList();

	private ClassLoader beanClassLoader;

	@Autowired(required = false)
	private ObjectPostProcessor<Object> objectObjectPostProcessor;

	@Bean
	@DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public SecurityExpressionHandler<FilterInvocation> webSecurityExpressionHandler() {
		return this.webSecurity.getExpressionHandler();
	}

	/**
	 * Creates the Spring Security Filter Chain
	 * @return the {@link Filter} that represents the security filter chain
	 * @throws Exception
	 */
	@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public Filter springSecurityFilterChain() throws Exception {
		// 如果我们自定义了配置类,这里就为 true
		boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();
		// 默认 securityFilterChain 为空,所以这里为 false
		boolean hasFilterChain = !this.securityFilterChains.isEmpty();
		Assert.state(!(hasConfigurers && hasFilterChain),
				"Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one.");
		if (!hasConfigurers && !hasFilterChain) {
			WebSecurityConfigurerAdapter adapter = this.objectObjectPostProcessor
					.postProcess(new WebSecurityConfigurerAdapter() {
					});
			this.webSecurity.apply(adapter);
		}
		for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
			this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
			for (Filter filter : securityFilterChain.getFilters()) {
				if (filter instanceof FilterSecurityInterceptor) {
					this.webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);
					break;
				}
			}
		}
		// WebSecurity 的一些自定义配置
		for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
			customizer.customize(this.webSecurity);
		}
		// 最后调用 WebSecurity 的 build 方法(重点)!!!
		// 整篇文章都在讲这个 build 方法,截止到文章末尾,这个方法也就走完了
		return this.webSecurity.build();
	}

	 // 设置实例的 WebSecurity 属性,以及 webSecurityConfigurers
	@Autowired(required = false)
	public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor,
			// 获取 WebSecurityConfigurers,配置类集合
			@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
			throws Exception {
			// 先创建一个 WebSecurity 对象
		this.webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
		if (this.debugEnabled != null) {
			this.webSecurity.debug(this.debugEnabled);
		}
		// 对配置类集合进行排序
		webSecurityConfigurers.sort(AnnotationAwareOrderComparator.INSTANCE);
		...
		
		for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
			// 将配置类中的信息加入到 WebSecurity 中
			this.webSecurity.apply(webSecurityConfigurer);
		}
		this.webSecurityConfigurers = webSecurityConfigurers;
	}

	// 这里的 securityFilterChains 为 null
	@Autowired(required = false)
	void setFilterChains(List<SecurityFilterChain> securityFilterChains) {
		// 设置 securityFilterChains  属性
		this.securityFilterChains = securityFilterChains;
	}

	// 这里的 webSecurityCustomizers 也为 null
	@Autowired(required = false)
	void setWebSecurityCustomizers(List<WebSecurityCustomizer> webSecurityCustomizers) {
		this.webSecurityCustomizers = webSecurityCustomizers;
	}

	// 获取配置类集合:WebSecurityConfigurers
	@Bean
	public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
			// 这里会自动注入:ConfigurableListableBeanFactory
			ConfigurableListableBeanFactory beanFactory) {
		return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
	}

	...
}

我们首先查看一下 AutowiredWebSecurityConfigurersIgnoreParents

AutowiredWebSecurityConfigurersIgnoreParents

public final class AutowiredWebSecurityConfigurersIgnoreParents {

	private final ConfigurableListableBeanFactory beanFactory;

	AutowiredWebSecurityConfigurersIgnoreParents(ConfigurableListableBeanFactory beanFactory) {
		Assert.notNull(beanFactory, "beanFactory cannot be null");
		this.beanFactory = beanFactory;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
		List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<>();
		// 获取容器中类型为:WebSecurityConfigurer 的 Bean
		Map<String, WebSecurityConfigurer> beansOfType = this.beanFactory.getBeansOfType(WebSecurityConfigurer.class);
		for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
			webSecurityConfigurers.add(entry.getValue());
		}
		return webSecurityConfigurers;
	}

}

通过 Debug 可以看到,getWebSecurityConfigurers 方法返回的就是我们继承自 WebSecurityConfigurerAdapter 的自定义配置类。

核心流程WebSecurityConfiguration 中的 springSecurityFilterChain() 方法,创建了一个名为 springSecurityFilterChain 的 Bean,同时如果我们没有自定义配置的话,会初始化一个 WebSecurityConfigurerAdapter 作为 WebSecurity

@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
	boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();
	boolean hasFilterChain = !this.securityFilterChains.isEmpty();
	Assert.state(!(hasConfigurers && hasFilterChain),
			"Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one.");
	if (!hasConfigurers && !hasFilterChain) {
		// 没有自定义配置类的话,会初始化一个 WebSecurityConfigurerAdapter
		WebSecurityConfigurerAdapter adapter = this.objectObjectPostProcessor
				.postProcess(new WebSecurityConfigurerAdapter() {
				});
		this.webSecurity.apply(adapter);
	}
	...

	// 核心流程,返回一个 Filter
	return this.webSecurity.build();
}

接下来会调用 WebSecuritybuild() 方法,来建立一个 Filter 对象对我们的请求进行拦截,从 Spring 官方文档 中我们知道,这里的 Filter 并不是 Servlet 中一个普通的 Filter 这么简单,这个 Filter 是一个代理的 FilterDelegatingFilterProxy),里面还会有过滤器链(SecurityFilterChain

SpringSecurity - 启动流程分析(一)_第1张图片

AbstractConfiguredSecurityBuilder

在查看 WebSecurity 中的 build 方法之前,我们先来看一下 apply 方法,入参在没有自定义配置的时候是 WebSecurityConfigurerAdapter

// AbstractConfiguredSecurityBuilder

public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
	add(configurer);
	return configurer;
}

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 (this.configurers) {
		if (this.buildState.isConfigured()) {
			throw new IllegalStateException("Cannot apply " + configurer + " to already built object");
		}
		List<SecurityConfigurer<O, B>> configs = null;
		if (this.allowConfigurersOfSameType) {
			configs = this.configurers.get(clazz);
		}
		configs = (configs != null) ? configs : new ArrayList<>(1);
		configs.add(configurer);
		// 保存配置类集合
		this.configurers.put(clazz, configs);
		if (this.buildState.isInitializing()) {
			this.configurersAddedInInitializing.add(configurer);
		}
	}
}

AbstractSecurityBuilder

WebSecuritybuild() 方法:

@Override
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");
}

// 由子类实现
protected abstract O doBuild() throws Exception;
// AbstractConfiguredSecurityBuilder

// 第一次进入这个方法,`this.configurers` 应该是我们自定义的配置类,如果没有自定义配置类,那应该是 `WebSecurityConfigurerAdapter`
@Override
protected final O doBuild() throws Exception {
	synchronized (this.configurers) {
		this.buildState = BuildState.INITIALIZING;
		beforeInit();
		// 核心流程1
		init();
		this.buildState = BuildState.CONFIGURING;
		beforeConfigure();
		configure();
		this.buildState = BuildState.BUILDING;
		// 核心流程2
		O result = performBuild();
		this.buildState = BuildState.BUILT;
		return result;
	}
}

// init() 方法调用的是自定义配置类的 init() 方法
private void init() throws Exception {
	Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
	for (SecurityConfigurer<O, B> configurer : configurers) {
		// 核心流程,这里的 init 方法,走的是 WebSecurityConfigurerAdapter 中的 init 方法
		configurer.init((B) this);
	}
	for (SecurityConfigurer<O, B> configurer : this.configurersAddedInInitializing) {
		configurer.init((B) this);
	}
}

protected abstract O performBuild() throws Exception;

WebSecurity

@Override
protected Filter performBuild() throws Exception {
	// 这里的 securityFilterChainBuilders 那里来的,还不能为空?
	Assert.state(!this.securityFilterChainBuilders.isEmpty(),
			() -> "At least one SecurityBuilder needs to be specified. "
					+ "Typically this is done by exposing a SecurityFilterChain bean "
					+ "or by adding a @Configuration that extends WebSecurityConfigurerAdapter. "
					+ "More advanced users can invoke " + WebSecurity.class.getSimpleName()
					+ ".addSecurityFilterChainBuilder directly");
	int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
	List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
	for (RequestMatcher ignoredRequest : this.ignoredRequests) {
		securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
	}
	// 这里的 securityFilterChainBuilders 经过上面的 init 方法之后,里面是一个 HttpSecurity
	for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
		// 这里通过 HttpSecurity 的 build 方法,转换为了 SecurityFilterChain,在源码中可以看到 build 方法最终返回的是 DefaultSecurityFilterChain
		securityFilterChains.add(securityFilterChainBuilder.build());
	}

	// 把上面收集到的 securityFilterChains 赋值给 FilterChainProxy
	FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
	if (this.httpFirewall != null) {
		filterChainProxy.setFirewall(this.httpFirewall);
	}
	if (this.requestRejectedHandler != null) {
		filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler);
	}
	filterChainProxy.afterPropertiesSet();

	Filter result = filterChainProxy;
	if (this.debugEnabled) {
		this.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);
	}
	this.postBuildAction.run();
	return result;
}

这里保留一个疑问:performBuild() 方法中 securityFilterChainBuilders 哪里来的?

WebSecurityConfigurerAdapter

WebSecurity 中的 build() 方法会调用 WebSecurityConfigurerAdapter 类的 init() 方法:

@Override
public void init(WebSecurity web) throws Exception {
	// 获取 HttpSecurity
	HttpSecurity http = getHttp();
	// 核心流程 addSecurityFilterChainBuilder,就是把 HttpSecurity 给添加到了 WebSecurity 的 securityFilterChainBuilder 属性中
	web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
		FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);
		web.securityInterceptor(securityInterceptor);
	});
}

通过 getHttp() 方法获取到 HttpSecurity 对象之后,使用 addSecurityFilterChainBuilderWebSecurity 中的 securityFilterChainBuilders 变量进行了赋值,在 WebSecurity 中的 performBuild() 方法中又把这个 securityFilterChainBuilders 转换为了 FilterChainProxy,从这里知道,我们会在 HttpSecurity 中组合 Filters。

// WebSecurity

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

到目前为止,虽然还没有把整个的流程给分析完,但是已经基本上把前期的准备工作,和一些 Bean 的创建以及关联说了一下,整个过程如下面的流程图:

SpringSecurity - 启动流程分析(一)_第2张图片

下篇文章: SpringSecurity - 启动流程分析(二)

你可能感兴趣的:(#,SpringSecurity,学习,SpringSecurity)