Today, we continue to lu Spring Security source to lu is a very important WebSecurityConfigurerAdapter.

Our custom are inherited from WebSecurityConfigurerAdapter, but for WebSecurityConfigurerAdapter the working principle of the internal configuration principle, a lot of friend may be still not very familiar with, so today we’ll le le.

We first see a WebSecurityConfigurerAdapter inheritance relationship diagram:

There are two very important classes in this hierarchy:

  • SecurityBuilder
  • SecurityConfigurer

Both of these classes have been shared with you in previous articles by Songo:

  • HttpSecurity (httpBuilder)
  • Learn more about SecurityConfigurer

So about the introduction of these two classes and their role, songge here will not be repeated. Let’s start with WebSecurityConfigurer.

1.WebSecurityConfigurer

WebSecurityConfigurer is an empty interface, but it has some generic constraints, like the following:

public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends
		SecurityConfigurer<Filter.T> {}Copy the code

Generics are the key to what WebSecurityConfigurer is for!

  1. The generic Filter in SecurityBuilder indicates that the ultimate purpose of the SecurityBuilder is to build a Filter object.
  2. The first of the two generics in SecurityConfigurer represents the object that SecurityBuilder will eventually build.

At the same time here also defines a new generic type T, T need to inherit from SecurityBuilder, according to the definition of the WebSecurityConfigurerAdapter, we can know, T is the WebSecurity, We can probably guess that WebSecurity is a subclass of SecurityBuilder.

The purpose of WebSecurityConfigurer is to configure WebSecurity.

2.WebSecurity

Let’s take a look at the definition of WebSecurity:

public final class WebSecurity extends
		AbstractConfiguredSecurityBuilder<Filter.WebSecurity> implements
		SecurityBuilder<Filter>, ApplicationContextAware {}Copy the code

Yes, it is! WebSecurity inherited from AbstractConfiguredSecurityBuilder < Filter, WebSecurity > implement the SecurityBuilder interface at the same time.

WebSecurity of these interfaces and inheritance classes, Songge in front of the source code analysis and we have been introduced, may be some partners forget, I will review with you.

AbstractConfiguredSecurityBuilder

First AbstractConfiguredSecurityBuilder defines an enumeration type, the entire build process can be divided into five kinds of state, can also be understood as the build process life cycle of five stages, as follows:

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(a) {
		return INITIALIZING.order == order;
	}
	public boolean isConfigured(a) {
		returnorder >= CONFIGURING.order; }}Copy the code

The five states are UNBUILT, INITIALIZING, CONFIGURING, BUILDING, and BUILT. Two other methods are provided, isInitializing to determine whether initialization is in progress and isConfigured to determine whether the initialization is complete.

AbstractConfiguredSecurityBuilder methods in more, scene out there in the two key methods and analysis:

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); }}}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;
}
Copy the code

The first is the Add method, which collects all the configuration classes. Configurers is a LinkedHashMap, key is a class of configuration classes, value is a collection of configuration classes, Inside the collection is the xxxConfigure configuration class. When these configuration classes need to be centrally configured, they are retrieved through the getConfigurers method, which takes the value from the LinkedHashMap and returns it in a collection.

Another method is the doBuild method.

@Override
protected final O doBuild(a) throws Exception {
	synchronized (configurers) {
		buildState = BuildState.INITIALIZING;
		beforeInit();
		init();
		buildState = BuildState.CONFIGURING;
		beforeConfigure();
		configure();
		buildState = BuildState.BUILDING;
		O result = performBuild();
		buildState = BuildState.BUILT;
		returnresult; }}private void init(a) 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); }}private void configure(a) throws Exception {
	Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
	for (SecurityConfigurer<O, B> configurer : configurers) {
		configurer.configure((B) this); }}Copy the code

In AbstractSecurityBuilder classes, filter building is shifted to the doBuild method, but in AbstractSecurityBuilder we just define the abstract doBuild method, Specific implementation in AbstractConfiguredSecurityBuilder.

The doBuild method updates the state while doing the initialization.

BeforeInit is a reserved method without any implementation.

The init method is to find all xxxConfigure and call its init method one by one to initialize it.

BeforeConfigure is a reserved method without any implementation.

The configure method finds all xxxConfigure and calls its configure method one by one.

Finally is performBuild method, method of building is the real filter chain, but in AbstractConfiguredSecurityBuilder performBuild method is just an abstract method, specific implementation in its subclass, Also known as WebSecurityConfigurer.

SecurityBuilder

SecurityBuilder is used to construct filter chain, realized in HttpSecurity SecurityBuilder, incoming generics is DefaultSecurityFilterChain, So the SecurityBuilder#build method is explicitly used to build a chain of filters, but that chain is in Spring Security. Is defined in WebSecurityConfigurerAdapter generic SecurityBuilder, so it’s the building is a common Filter, is actually named FilterChainProxy, about named FilterChainProxy, See FilterChainProxy for more information.

WebSecurity

The core logic of WebSecurity centers on the performBuild build method. Let’s take a look at it:

@Override
protected Filter performBuild(a) 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;
}
Copy the code

The performBuild method has only one function, which is to build FilterChainProxy. If you don’t know what FilterChainProxy is, you can refer to songo’s previous introduction: In-depth understanding of FilterChainProxy.

With this thread in mind, it’s easy to look at the implementation of the method.

  1. IgnoredRequests (quests) specifies the number of filter chains to be added to. This includes “ignoredRequests” (quests) that derive from WebSecurity. , the other is securityFilterChainBuilders, that is, through the filter chain HttpSecurity configuration, we have a few even if a few.
  2. Create the securityFilterChains collection and iterate through the two types of filter chains mentioned above and place the filter chains into the securityFilterChains collection.
  3. I am understanding HttpSecurity [source article], the article introduced by HttpSecurity constructed filter chain object is DefaultSecurityFilterChain, IgnoredRequests can be added to securityFilterChains, whereas those saved in ignoredRequests can be added to securityFilterChains.
  4. Once you have the data in securityFilterChains, create a FilterChainProxy.
  5. Configure a firewall for FilterChainProxy. For details on the firewall, see Spring Security’s built-in firewall. You have no idea how secure your system is! .
  6. Finally, we return an instance of FilterChainProxy.

WebSecurity and HttpSecurity differ from each other.

  1. The purpose of HttpSecurity is to build a chain of filters. An HttpSecurity object builds a chain of filters. A chain of filters contains N filters.
  2. The purpose of WebSecurity is to build FilterChainProxy. A FilterChainProxy contains multiple filter chains and a Firewall.

This is the main function of WebSecurity, the core method is performBuild, other methods are relatively simple, songko will not explain.

3.WebSecurityConfigurerAdapter

Finally, we look at WebSecurityConfigurerAdapter, because WebSecurityConfigurer is just an empty interface, WebSecurityConfigurerAdapter is aimed at the empty interface provides a concrete implementation, The ultimate goal is to make it easier for you to configure WebSecurity.

WebSecurityConfigurerAdapter methods in more, but according to our previous analysis, commanding summation method has two, one is init, there is also a configure (WebSecurity web), other methods are to serve these two methods. Let’s take a look at these two methods:

First look at the init method:

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(a) throws Exception {
	if(http ! =null) {
		returnhttp; } 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();
}
Copy the code

The init method can be considered the entry method here: first call the getHttp method to initialize HttpSecurity. When you initialize HttpSecurity, you configure a bunch of default filters, and then you end up calling the Configure (HTTP) method, which in turn configures some interceptors, but in practice we often override the configure(HTTP) method, The configure(HTTP) method has almost always been rewritten in previous articles in Songo’s series. HttpSecurity configuration is complete, then HttpSecurity into the WebSecurity, save in the WebSecurity securityFilterChainBuilders collection, specific see: Understand HttpSecurity in depth.

The configure(WebSecurity Web) method is actually an empty method, and we may have to rewrite it in real development. ) :

public void configure(WebSecurity web) throws Exception {}Copy the code

4. Summary

This is WebSecurityConfigurerAdapter, whole is not difficult, but to and in front of the scene a few article source code analysis, together will understand more deeply.

Portal:

  1. FilterChainProxy
  2. Learn more about SecurityConfigurer
  3. Understanding HttpSecurity
  4. Deep understanding of AuthenticationManagerBuilder [source article]

Well, if you have a harvest, remember to click on the encouragement of songge oh ~