Spring Security系列-Spring Security生命週期之誕生

前言

接着上一篇,我們來談談WebSecurity的規則是如何從我們配置規則加入到整個SpringSecurity的認證鏈條的。

配置

回顧一下上一篇那個簡單的WebSecurity配置

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http
            .authorizeRequests()
                .anyRequest().authenticated()
            .and()
                .httpBasic();
        // @formatter:on
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        // @formatter:off
        auth
            .inMemoryAuthentication()
                .withUser("admin")
                .password(passwordEncoder.encode("123456"))
                .authorities("admin");
        // @formatter:on
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/css/**", "/js/**", "/favicon.ico");
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
}

配置方法

上面的三個config配置方法裏,應用了許多配置項。
比如httpBasic()方法,從下面的源代碼可以看出,它會實例化HttpBasicConfigurer類,並應用到HttpSecurity裏。

public HttpBasicConfigurer<HttpSecurity> httpBasic() throws Exception {
	return getOrApply(new HttpBasicConfigurer<>());
}

又比如inMemoryAuthentication()方法。

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

無論是HttpBasicConfigurer還是InMemoryUserDetailsManagerConfigurer,都有同一個父類SecurityBuilder。

builder設計模式

SecurityBuilder類是一個建造者類,只有一個build方法。

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

SecurityBuilder有衆多繼承類,下面展示了其中的一部分。
在這裏插入圖片描述
還記得上一篇setFilterChainProxySecurityConfigurer方法創建的webSeurity對象,它也繼承SecurityBuilder類。當webSeurity.build()後,就會引發它下面所有的SecurityBuild繼承類的調用build方法,如上面說到的HttpBasicConfigurer和InMemoryUserDetailsManagerConfigurer類。

誕生

開始進入主題,看看webSecurity.build()是怎麼一步步應用我們的配置三個方法的。

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

1. 三個自定義配置

1.1 configure(AuthenticationManagerBuilder auth)

先看看是如何一步步來到的configure(AuthenticationManagerBuilder auth)方法。
WebSecurity.build() ->
AbstractSecurityBuilder.build() ->
AbstractConfiguredSecurityBuilder.doBuild() ->
AbstractConfiguredSecurityBuilder.init()->
WebSecurityConfigurerAdapter.init(final WebSecurity web)->
WebSecurityConfigurerAdapter.getHttp()->
WebSecurityConfigurerAdapter.authenticationManager()->
自定義WebSecurityConfig->configure(AuthenticationManagerBuilder auth)

1.2 configure(HttpSecurity http)

然後是來到的configure(HttpSecurity http)方法。
WebSecurity.build() ->
AbstractSecurityBuilder.build() ->
AbstractConfiguredSecurityBuilder.doBuild() ->
AbstractConfiguredSecurityBuilder.init()->
WebSecurityConfigurerAdapter.init(final WebSecurity web)->
WebSecurityConfigurerAdapter.getHttp()->
自定義WebSecurityConfig->configure(HttpSecurity http)

1.3 configure(WebSecurity web)

最後是來到的configure(WebSecurity http)方法。
WebSecurity.build() ->
AbstractSecurityBuilder.build() ->
AbstractConfiguredSecurityBuilder.doBuild() ->
AbstractConfiguredSecurityBuilder.configure()->
自定義WebSecurityConfig->configure(WebSecurity web)

FilterChainProxy

在springSecurityFilterChain Bean的構建中,會調用下面的performBuild()方法。於是就創建了FilterChainProxy實例,並會添加我們自定義配置到securityFilterChains中。ignoredRequests就是我們配置configure(WebSecurity web)方法中的web.ignoring().antMatchers("/css/", "/js/", “/favicon.ico”);而securityFilterChainBuilders就是我們配置的configure(HttpSecurity http)。

protected Filter performBuild() throws Exception {
	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);

	Filter result = filterChainProxy;
	postBuildAction.run();
	return result;
}

2. ignoredRequest

下圖可以清楚的看到3個ignoredRequest
在這裏插入圖片描述

3. securityFilterChain

securityFilterChain情況就比較複雜了,會合並我們自定義配置和默認配置

3.1默認配置

在WebSecurity.build()方法被調用時,還有一段SpringSecurity設置默認配置的代碼,如下

protected final HttpSecurity getHttp() throws Exception {
	...
	http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
			sharedObjects);
	if (!disableDefaults) {
		// @formatter:off
		http
			.csrf().and()                                       // 1
			.addFilter(new WebAsyncManagerIntegrationFilter())  // 2
			.exceptionHandling().and()                          // 3
			.headers().and()                                    // 4
			.sessionManagement().and()                          // 5
			.securityContext().and()                            // 6
			.requestCache().and()                               // 7
			.anonymous().and()                                  // 8
			.servletApi().and()                                 // 9
			.apply(new DefaultLoginPageConfigurer<>()).and()    //10
			.logout();                                          //11
		// @formatter:on
		...
	}
	configure(http);
	return http;
}

從上面SpringSecurity提供的默認配置可以看出,SpringSecurity默認地爲我們添加了11個SecurityConfigurer和Filter。

3.2 SecurityConfigurer和Filter

以SpringSecurity提供的默認配置,csrf()方法爲例。
首先,csrf()方法應用了一個CsrfConfigurer配置類,這個類繼承自SecurityConfigurer

public final class HttpSecurity extends
	AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
	implements SecurityBuilder<DefaultSecurityFilterChain>,
	HttpSecurityBuilder<HttpSecurity> {
	...
	public CsrfConfigurer<HttpSecurity> csrf() throws Exception {
		ApplicationContext context = getContext();
		return getOrApply(new CsrfConfigurer<>(context));
	}
	...
}		

接着,CsrfConfigurer實現了configure(H http)方法,此方法會實例化一個CsrfFilter。

public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
		extends AbstractHttpConfigurer<CsrfConfigurer<H>, H> {
	...
	@Override
	public void configure(H http) throws Exception {
		CsrfFilter filter = new CsrfFilter(this.csrfTokenRepository);
		RequestMatcher requireCsrfProtectionMatcher = getRequireCsrfProtectionMatcher();
		if (requireCsrfProtectionMatcher != null) {
			filter.setRequireCsrfProtectionMatcher(requireCsrfProtectionMatcher);
		}
		AccessDeniedHandler accessDeniedHandler = createAccessDeniedHandler(http);
		if (accessDeniedHandler != null) {
			filter.setAccessDeniedHandler(accessDeniedHandler);
		}
		LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);
		if (logoutConfigurer != null) {
			logoutConfigurer
					.addLogoutHandler(new CsrfLogoutHandler(this.csrfTokenRepository));
		}
		SessionManagementConfigurer<H> sessionConfigurer = http
				.getConfigurer(SessionManagementConfigurer.class);
		if (sessionConfigurer != null) {
			sessionConfigurer.addSessionAuthenticationStrategy(
					new CsrfAuthenticationStrategy(this.csrfTokenRepository));
		}
		filter = postProcess(filter);
		http.addFilter(filter);
	}
	...
}

最終,這個Filter會被添加到到http,而這個http就是HttpSecurity類,是本篇在開頭提到的getHttp()方法中實例出來的,同時也是我們的自定義配置類中可以看到的那個HttpSecurity http。

4. 最終產物

上篇提到的springSecurityFilterChain是一個Bean。如果按照我們自定義的配置,它會包括4個DefaultSecurityFilterChain。其中三個filterChain是我們配置的web.ignoring().antMatchers("/css/", "/js/", “/favicon.ico”),另外一個是我們配置的configure(HttpSecurity http)。HttpSecurity因爲保留了SpringSecurity的默認配置,所以會有我們配置之外的默認配置。
在這裏插入圖片描述
從上圖可以看出,每一個HttpRequest都會經過4個FilterChain。

總結

本篇介紹了springSecurityFilterChain的形成,它最終包含了4個filterChain。其中HttpSecurity生成的filterChainer,包含了多個filter。那麼下篇再談談這些Filter是怎麼幫助我們來攔截或者處理HttpRequest的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章