咱們繼續來擼 Spring Security 源碼,今天來擼一個很是重要的 WebSecurityConfigurerAdapter。java
咱們的自定義都是繼承自 WebSecurityConfigurerAdapter 來實現的,可是對於 WebSecurityConfigurerAdapter 內部的工做原理,配置原理,不少小夥伴可能都還不太熟悉,所以咱們今天就來捋一捋。web
咱們先來看一張 WebSecurityConfigurerAdapter 的繼承關係圖:安全
在這層繼承關係中,有兩個很是重要的類:session
這兩個類鬆哥在以前的文章中都和你們分享過了,具體參考:app
因此關於這兩個類的介紹以及做用,鬆哥這裏就不贅述了。我們直接從 WebSecurityConfigurer 開始看起。ide
WebSecurityConfigurer 實際上是一個空接口,可是它裏邊約束了一些泛型,以下:源碼分析
public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends SecurityConfigurer<Filter, T> { }
這裏邊的泛型很關鍵,這關乎到 WebSecurityConfigurer 的目的是啥!post
同時這裏還定義了新的泛型 T,T 須要繼承自 SecurityBuilder<Filter>,根據 WebSecurityConfigurerAdapter 中的定義,咱們能夠知道,T 就是 WebSecurity,咱們也大概能猜出 WebSecurity 就是 SecurityBuilder<Filter> 的子類。ui
因此 WebSecurityConfigurer 的目的咱們能夠理解爲就是爲了配置 WebSecurity。this
咱們來看下 WebSecurity 的定義:
public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity> implements SecurityBuilder<Filter>, ApplicationContextAware { }
沒錯,確實是這樣!WebSecurity 繼承自 AbstractConfiguredSecurityBuilder<Filter, WebSecurity> 同時實現了 SecurityBuilder<Filter> 接口。
WebSecurity 的這些接口和繼承類,鬆哥在前面的源碼分析中都和你們介紹過了,可能有的小夥伴忘記了,我再來和你們複習一下。
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(); 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; }
第一個就是這個 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(); init(); buildState = BuildState.CONFIGURING; beforeConfigure(); configure(); buildState = BuildState.BUILDING; O result = performBuild(); buildState = BuildState.BUILT; return result; } } 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); } } 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 方法只是一個抽象方法,具體的實如今它的子類中,也就是 WebSecurityConfigurer。
SecurityBuilder<Filter>
SecurityBuilder 就是用來構建過濾器鏈的,在 HttpSecurity 實現 SecurityBuilder 時,傳入的泛型就是 DefaultSecurityFilterChain,因此 SecurityBuilder#build 方法的功能很明確,就是用來構建一個過濾器鏈出來,可是那個過濾器鏈是 Spring Security 中的。在 WebSecurityConfigurerAdapter 中定義的泛型是 SecurityBuilder<Filter>,因此最終構建的是一個普通 Filter,其實就是 FilterChainProxy,關於 FilterChainProxy ,你們能夠參考深刻理解 FilterChainProxy【源碼篇】。
WebSecurity
WebSecurity 的核心邏輯集中在 performBuild 構建方法上,咱們一塊兒來看下:
@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,若是你還不瞭解什麼是 FilterChainProxy,能夠參考鬆哥以前的介紹:深刻理解 FilterChainProxy【源碼篇】。
把握住了這條主線,咱們再來看方法的實現就很容易了。
從這段分析中,咱們能夠看出來 WebSecurity 和 HttpSecurity 的區別:
這就是 WebSecurity 的主要做用,核心方法是 performBuild,其餘方法都比較簡單,鬆哥就不一一解釋了。
最後咱們再來看 WebSecurityConfigurerAdapter,因爲 WebSecurityConfigurer 只是一個空接口,WebSecurityConfigurerAdapter 就是針對這個空接口提供一個具體的實現,最終目的仍是爲了方便你配置 WebSecurity。
WebSecurityConfigurerAdapter 中的方法比較多,可是根據咱們前面的分析,提綱挈領的方法就兩個,一個是 init,還有一個 configure(WebSecurity web),其餘方法都是爲這兩個方法服務的。那咱們就來看下這兩個方法:
先看 init 方法:
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() throws Exception { if (http != null) { return http; } 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(); }
init 方法能夠算是這裏的入口方法了:首先調用 getHttp 方法進行 HttpSecurity 的初始化。HttpSecurity 的初始化,實際上就是配置了一堆默認的過濾器,配置完成後,最終還調用了 configure(http) 方法,該方法又配置了一些攔截器,不過在實際開發中,咱們常常會重寫 configure(http) 方法,在鬆哥本系列前面的文章中,configure(http) 方法幾乎都有重寫。HttpSecurity 配置完成後,再將 HttpSecurity 放入 WebSecurity 中,保存在 WebSecurity 的 securityFilterChainBuilders 集合裏,具體參見:深刻理解 HttpSecurity【源碼篇】。
configure(WebSecurity web) 方法其實是一個空方法,咱們在實際開發中可能會重寫該方法(參見 Spring Security 兩種資源放行策略,千萬別用錯了! 一文):
public void configure(WebSecurity web) throws Exception { }
這即是 WebSecurityConfigurerAdapter,總體上來講並不難,可是要和鬆哥前面幾篇源碼分析文章一塊兒看,理解會更加深入一些。
傳送門:
好啦,小夥伴們要是有收穫,記得點個在看鼓勵下鬆哥哦~