五SpringSecurity重要配置类--3HttpSecurityWebSecurity

五SpringSecurity重要配置类--3HttpSecurity/WebSecurity

HttpSecurity(build方法构造过滤器链)和WebSecurity(build->performBuild构造 FilterChainProxy)

image-20230822201244848

image-20230823023403298

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

从其继承和实现结构,可以看出使用泛型,主要是DefaultSecurityFilterChain和HttpSecurity。

过滤器链详细内容看4.3部分

4.5.1 父类 SecurityBuilder--O build()唯一方法
public interface SecurityBuilder<O> {
  //在 HttpSecurity 实现 SecurityBuilder 时,传入的泛型就是 DefaultSecurityFilterChain,所以 SecurityBuilder#build 方法的功能很明确,就是用来构建一个过滤器链出来。
	O build() throws Exception;
}

SecurityBuilder 就是用来构建过滤器链的,在 HttpSecurity 实现 SecurityBuilder 时,传入的泛型就是 DefaultSecurityFilterChain,所以 SecurityBuilder#build 方法的功能很明确,就是用来构建一个过滤器链出来。

4.5.2 HttpSecurityBuilder

HttpSecurityBuilder 看名字就是用来构建 HttpSecurity 的。不过它也只是一个接口,具体的实现在 HttpSecurity 中,接口定义如下:

public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> extends
		SecurityBuilder<DefaultSecurityFilterChain> {
  //获取SecurityConfigurer<DefaultSecurityFilterChain, H>
	<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);
  
  //配置验证器AuthenticationProvider
	H authenticationProvider(AuthenticationProvider authenticationProvider);
  //userDetailsService 配置数据源接口
	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. getConfigurer 获取一个配置对象。Spring Security 过滤器链中的所有过滤器对象都是由 xxxConfigure 来进行配置的,这里就是获取这个 xxxConfigure 对象。
  2. removeConfigurer 移除一个配置对象。
  3. setSharedObject/getSharedObject 配置/获取由多个 SecurityConfigurer 共享的对象。
  4. authenticationProvider 方法表示配置验证器。
  5. userDetailsService 配置数据源接口。
  6. addFilterAfter 在某一个过滤器之前添加过滤器。
  7. addFilterBefore 在某一个过滤器之后添加过滤器。
  8. addFilter 添加一个过滤器,该过滤器必须是现有过滤器链中某一个过滤器或者其扩展。

这便是 HttpSecurityBuilder 中的功能,这些接口在 HttpSecurity 中都将得到实现。

4.5.3 AbstractSecurityBuilder

AbstractSecurityBuilder 类实现了 SecurityBuilder 接口,该类中主要做了一件事,就是确保整个构建只被构建一次。

public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
  //实现build方法只被调用一次,用来构造过滤器链
	private AtomicBoolean building = new AtomicBoolean();
  //securityFilterChain对象
	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;
}

可以看到,这里重新定义了 build 方法,并设置 build 方法为 final 类型,无法被重写,在 build 方法中,通过 AtomicBoolean 实现该方法只被调用一次。具体的构建逻辑则定义了新的抽象方法 doBuild,将来在实现类中通过 doBuild 方法定义构建逻辑。

4.5.4 AbstractConfiguredSecurityBuilder

AbstractSecurityBuilder 方法的实现类就是 AbstractConfiguredSecurityBuilder。

AbstractConfiguredSecurityBuilder 中所做的事情就比较多了,我们分别来看。

首先 AbstractConfiguredSecurityBuilder 中定义了一个枚举类,将整个构建过程分为 5 种状态,也可以理解为构建过程生命周期的五个阶段,如下:

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 中的方法比较多,列出来两个关键的方法分析:

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();
  
  //configurers结构是hashmap<clazz,List<SecurityConfigurer<O, B>>>,集合里边放着 xxxConfigure 配置类
	synchronized (configurers) {
		if (buildState.isConfigured()) {
			throw new IllegalStateException("Cannot apply " + configurer
					+ " to already built object");
		}
    //是否允许对同一个type配置多个configurers
		List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers
				.get(clazz) : null;
		if (configs == null) {
			configs = new ArrayList<>(1);
		}
		configs.add(configurer);
    //将clazz和list的对应关系,存入LinkedHashMap
		this.configurers.put(clazz, configs);
    
    //当前状态是isInitializing,就添加待add 的configurer添加入初始化队列中
		if (buildState.isInitializing()) {
			this.configurersAddedInInitializing.add(configurer);
		}
	}
}


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

第一个就是这个 add 方法,这相当于是在收集所有的配置类。将所有的 xxxConfigure 收集起来存储到 configurers 中,将来再统一初始化并配置,configurers 本身是一个 LinkedHashMap ,key 是配置类的 class,value 是一个集合,集合里边放着 xxxConfigure 配置类。当需要对这些配置类进行集中配置的时候,会通过 getConfigurers 方法获取配置类,这个获取过程就是把 LinkedHashMap 中的 value 拿出来,放到一个集合中返回。

另一个方法就是 doBuild 方法。

@Override
protected final O doBuild() throws Exception {
	synchronized (configurers) {
		buildState = BuildState.INITIALIZING;
		beforeInit();
    //初始化xxxConfigurer
		init();
		buildState = BuildState.CONFIGURING;
		beforeConfigure();
    //执行xxxConfigurer的configure()方法
		configure();
		buildState = BuildState.BUILDING;
    
    //真正构造过滤器链securityFilterChain方法,在httpSecurity中实现
		O result = performBuild();
		buildState = BuildState.BUILT;
		return result;
	}
}

//分别执行所有xxxConfigurer的初始化init方法
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);
	}
}

//分别执行所有xxxConfigurer的配置configure方法
private void configure() throws Exception {
	Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
	for (SecurityConfigurer<O, B> configurer : configurers) {
		configurer.configure((B) this);
	}
}

在 AbstractSecurityBuilder 类中,过滤器的构建被转移到 doBuild 方法上面了,不过在 AbstractSecurityBuilder 中只是定义了抽象的 doBuild 方法,具体的实现在 AbstractConfiguredSecurityBuilder。

doBuild 方法就是一边更新状态,进行进行初始化。

beforeInit 是一个预留方法,没有任何实现。

init 方法就是找到所有的 xxxConfigure,挨个调用其 init 方法进行初始化。

beforeConfigure 是一个预留方法,没有任何实现。

configure 方法就是找到所有的 xxxConfigure,挨个调用其 configure 方法进行配置。

最后则是 performBuild 方法,是真正的过滤器链构建方法,但是在 AbstractConfiguredSecurityBuilder 中 performBuild 方法只是一个抽象方法,具体的实现在 HttpSecurity 中。
4.5.5 HttpSecurity--(进行xxConfigurer配置、performBuild构造过滤器链)

HttpSecurity 做的事情,就是进行各种各样的 xxxConfigurer 配置。

随便举几例:

public CorsConfigurer<HttpSecurity> cors() throws Exception {
	return getOrApply(new CorsConfigurer<>());
}
public CsrfConfigurer<HttpSecurity> csrf() throws Exception {
	ApplicationContext context = getContext();
	return getOrApply(new CsrfConfigurer<>(context));
}
public ExceptionHandlingConfigurer<HttpSecurity> exceptionHandling() throws Exception {
	return getOrApply(new ExceptionHandlingConfigurer<>());
}

HttpSecurity 中有大量类似的方法,过滤器链中的过滤器就是这样一个一个配置的。我就不一一介绍了。

每个配置方法的结尾都会来一句 getOrApply,这个是干嘛的?

private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply(
		C configurer) throws Exception {
  
  //在它的父类 AbstractConfiguredSecurityBuilder 中定义的,目的就是去查看当前这个 xxxConfigurer 是否已经配置过了。
	C existingConfig = (C) getConfigurer(configurer.getClass());
	if (existingConfig != null) {
		return existingConfig;
	}
	return apply(configurer);
}


AbstractConfiguredSecurityBuilder
  	public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception {
  //向每个xxxConfigurer中添加objectPostProcessor,
		configurer.addObjectPostProcessor(this.objectPostProcessor);
		configurer.setBuilder((B) this);
		add(configurer);
		return configurer;
	}


getConfigurer 方法是在它的父类 AbstractConfiguredSecurityBuilder 中定义的,目的就是去查看当前这个 xxxConfigurer 是否已经配置过了。

如果当前 xxxConfigurer 已经配置过了,则直接返回,否则调用 apply 方法,这个 apply 方法最终会调用到 AbstractConfiguredSecurityBuilder#add 方法,将当前配置 configurer 收集起来。

HttpSecurity 中还有一个 addFilter 方法:

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

这个 addFilter 方法的作用,主要是在各个 xxxConfigurer 进行配置的时候,会调用到这个方法,(xxxConfigurer 就是用来配置过滤器的),把 Filter 都添加到 fitlers 变量中。

最终在 HttpSecurity 的 performBuild 方法中,构建出来一个过滤器链:

@Override
protected DefaultSecurityFilterChain performBuild() {
  //按照comparator对filters排序
	filters.sort(comparator);
  //过滤器链
	return new DefaultSecurityFilterChain(requestMatcher, filters);
}

先给过滤器排序,然后构造 DefaultSecurityFilterChain 对象。

4.5.6 WebSecurity--(performBuild创建FilterChainProxy)

image-20230823023350039

我们来看下 WebSecurity 的定义:

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

没错,确实是这样!WebSecurity 继承自 AbstractConfiguredSecurityBuilder<Filter, WebSecurity> 同时实现了 SecurityBuilder 接口。

WebSecurity 的这些接口和继承类,在前面的源码分析中都和大家介绍过了。

区别:

SecurityBuilder 就是用来构建过滤器链的,在 HttpSecurity 实现 SecurityBuilder 时,传入的泛型就是 DefaultSecurityFilterChain,所以SecurityBuilder#build 方法的功能很明确,就是用来构建一个过滤器链出来,但是那个过滤器链是 Spring Security 中的;

在 WebSecurityConfigurerAdapter 中定义的泛型是 SecurityBuilder,所以最终构建的是一个普通 Filter,其实就是 FilterChainProxy,

WebSecurity 的核心逻辑集中在 performBuild 构建方法上,我们一起来看下:

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

先来说一句,这里的 performBuild 方法只有一个功能,那就是构建 FilterChainProxy。

把握住了这条主线,我们再来看方法的实现就很容易了。

  1. 首先统计过滤器链的总条数,总条数包括两个方面,一个是 ignoredRequests,这是忽略的请求,通过 WebSecurity 配置的忽略请求。另一个则是 securityFilterChainBuilders,也就是我们通过 HttpSecurity 配置的过滤器链,有几个就算几个。
  2. 创建 securityFilterChains 集合,并且遍历上面提到的两种类型的过滤器链,并将过滤器链放入 securityFilterChains 集合中。
  3. HttpSecurity 构建出来的过滤器链对象就是 DefaultSecurityFilterChain,所以可以直接将 build 结果放入 securityFilterChains 中,而 ignoredRequests 中保存的则需要重构一下才可以存入 securityFilterChains。
  4. securityFilterChains 中有数据之后,接下来创建一个 FilterChainProxy。
  5. 给新建的 FilterChainProxy 配置上防火墙。
  6. 最后我们返回的就是 FilterChainProxy 的实例。
总结:WebSecurity和HttpSecurity的区别

从这段分析中,我们可以看出来 WebSecurity 和 HttpSecurity 的区别:

  1. HttpSecurity 目的是构建过滤器链,一个 HttpSecurity 对象构建一条过滤器链,一个过滤器链中有 N 个过滤器,HttpSecurity 所做的事情实际上就是在配置这 N 个过滤器。
  2. WebSecurity 目的是构建 FilterChainProxy,一个 FilterChainProxy 中包含有多个过滤器链和一个 Firewall。

这就是 WebSecurity 的主要作用,核心方法是 performBuild。

posted @ 2023-08-28 22:59  LeasonXue  阅读(130)  评论(0)    收藏  举报