一、WebSecurityConfigurerAdapter是什么?

WebSecurityConfigurerAdapter是为创建WebSecurityConfigurer实例提供方便的基类,该类允许开发人员通过覆盖方法进行定制。
类图:
在这里插入图片描述
通过类图,可以了解到WebSecurityConfigurerAdapter实现了WebSecurityConfigurer的接口。

1.WebSecurityConfigurer是什么?

public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends SecurityConfigurer<Filter, T> {
}

WebSecurityConfigurer是一个空的接口,它允许对WebSecurity进行定义,目的就是配置一个WebSecurity。

2.WebSecurity

2.1 WebSecurity定义

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

可以看到WebSecurity继承了AbstractConfiguredSecurityBuilder并实现了SecurityBuilder。
之前分析了解到,WebSecurity由WebSecurityConfiguration创建,以创建称为 Spring Security Filter Chain (springSecurityFilterChain) 的FilterChainProxy 。

2.2 AbstractConfiguredSecurityBuilder

AbstractConfiguredSecurityBuilder维护了每一个子类的实现集合,例如我们探讨的WebSecurity,当我们定义了多个WebSecurityConfigurerAdapter时,会在全局变量configurers属性中维护具体配置。
维护源码:

	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);
			}
		}
	}
	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);
	}
  1. add()方法将传入的configer维护到了configers里,configurers 本身是一个 LinkedHashMap ,key 是配置类的 class,value 是一个集合,集合里边放着 xxxConfigure 配置类。
  2. 当需要对这些配置类进行集中配置的时候,会通过 getConfigurers 方法获取配置类,这个获取过程就是把 LinkedHashMap 中的 value 拿出来,放到一个集合中返回。

AbstractConfiguredSecurityBuilder中还有一个特别重要的方法就是doBuild方法。

	@Override
	protected final O doBuild() throws Exception {
		synchronized (this.configurers) {
			this.buildState = BuildState.INITIALIZING;
			beforeInit();
			init();
			this.buildState = BuildState.CONFIGURING;
			beforeConfigure();
			configure();
			this.buildState = BuildState.BUILDING;
			O result = performBuild();
			this.buildState = BuildState.BUILT;
			return result;
		}
	}
	private void configure() throws Exception {
		Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
		for (SecurityConfigurer<O, B> configurer : configurers) {
			configurer.configure((B) this);
		}
	}

doBuild方法的入口是自动配置WebSecurityConfiguration的springSecurityFilterChain方法:

@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 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;
				}
			}
		}
		for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
			customizer.customize(this.webSecurity);
		}
		return this.webSecurity.build();
	}

此处调用build方法,最终调用的是duBuild方法。
doBuild 方法就是一边更新状态,进行进行初始化。
beforeInit 是一个预留方法,没有任何实现。
init 方法就是找到所有的 xxxConfigure,挨个调用其 init 方法进行初始化。
beforeConfigure 是一个预留方法,没有任何实现。
configure 方法就是找到所有的 xxxConfigure,挨个调用其 configure 方法进行配置。
最后则是 performBuild 方法,是真正的过滤器链构建方法,但是在AbstractConfiguredSecurityBuilder 中 performBuild 方法只是一个抽象方法,具体的实现在它的子类中。

2.3 再次看WebSecurity

通过上面的分析,可以了解到最终调的是子类的performBuild方法,看下WebSecurity的核心逻辑performBuild方法:

	@Override
	protected Filter performBuild() throws Exception {
		Assert.state(!this.securityFilterChainBuilders.isEmpty(),
				() -> "At least one SecurityBuilder<? extends SecurityFilterChain> 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));
		}
		for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
			securityFilterChains.add(securityFilterChainBuilder.build());
		}
		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;
	}

从方法的返回值可以看出,返回的是一个Filter。

  1. 首先统计过滤器链的总条数,包括ignoredRequests(忽略的请求,通过WebSecurity配置),另外一个是securityFilterChainBuilders(通过HttpSecurity配置的过滤器链)
  2. 创建securityFilterChains的集合,将上面两个集合分别加入到此securityFilterChains中。
  3. 再通过securityFilterChains创建FilterChainProxy的实例并设置防火墙和拒绝策略的handler
  4. 最后返回这个FilterChainProxy实例

3 WebSecurityConfigurerAdapter

WebSecurityConfigurerAdapter 中的方法比较多,但是根据我们前面的分析,提纲挈领的方法就两个,一个是 init,还有一个 configure(WebSecurity web),其他方法都是为这两个方法服务的。那我们就来看下这两个方法:

	@Override
	public void init(WebSecurity web) throws Exception {
		HttpSecurity http = getHttp();
		web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
			FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);
			web.securityInterceptor(securityInterceptor);
		});
	}
	
	protected final HttpSecurity getHttp() throws Exception {
		if (this.http != null) {
			return this.http;
		}
		AuthenticationEventPublisher eventPublisher = getAuthenticationEventPublisher();
		this.localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
		AuthenticationManager authenticationManager = authenticationManager();
		this.authenticationBuilder.parentAuthenticationManager(authenticationManager);
		Map<Class<?>, Object> sharedObjects = createSharedObjects();
		this.http = new HttpSecurity(this.objectPostProcessor, this.authenticationBuilder, sharedObjects);
		if (!this.disableDefaults) {
			applyDefaultConfiguration(this.http);
			ClassLoader classLoader = this.context.getClassLoader();
			List<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader
					.loadFactories(AbstractHttpConfigurer.class, classLoader);
			for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
				this.http.apply(configurer);
			}
		}
		configure(this.http);
		return this.http;
	}

	protected void configure(HttpSecurity http) throws Exception {
		this.logger.debug("Using default configure(HttpSecurity). "
				+ "If subclassed this will potentially override subclass configure(HttpSecurity).");
		http.authorizeRequests((requests) -> requests.anyRequest().authenticated());
		http.formLogin();
		http.httpBasic();
	}

init 方法可以算是这里的入口方法了:首先调用 getHttp 方法进行 HttpSecurity 的初始化。HttpSecurity 的初始化,实际上就是配置了一堆默认的过滤器,配置完成后,最终还调用了 configure(http) 方法,该方法又配置了一些拦截器,不过在实际开发中,我们经常会重写 configure(http) 方法。

	@Override
	public void configure(WebSecurity web) throws Exception {
	}

configure(WebSecurity web) 方法实际上是一个空方法,覆盖此方法以配置WebSecurity 。 例如,如果您希望忽略某些请求。 Spring Security 将忽略此方法中指定的端点,这意味着它不会保护它们免受 CSRF、XSS、Clickjacking 等攻击。 相反,如果您想保护端点免受常见漏洞的影响。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐