上篇文章通過對WebSecurityConfiguration
這個配置類的源碼閱讀,已經(jīng)了解到,在啟動的時候主要創(chuàng)建了兩個對象津辩,WebSecurity
和名字為springSecurityFilterChain
的Filter
。這篇文章主要是通過源碼閱讀,查看一下Filter的創(chuàng)建過程销部,以及后面的工作原理。
1. Filter的創(chuàng)建
1.1再看Filter的創(chuàng)建
/**
* Creates the Spring Security Filter Chain
* @return the {@link Filter} that represents the security filter chain
* @throws Exception
*/
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
//T1 查看是否有WebSecurityConfigurer的相關配置
boolean hasConfigurers = webSecurityConfigurers != null
&& !webSecurityConfigurers.isEmpty();
//T2 如果沒有制跟,說明我們沒有注入繼承WebSecurityConfigurerAdapter 的對象
if (!hasConfigurers) {
//T3 創(chuàng)建默認的配置信息WebSecurityConfigurerAdapter ,保證Spring Security的最基礎的功能舅桩,如果我們要有自定義的相關,一定要重寫配置
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
//T4 默認配置信息載入webSecurity
webSecurity.apply(adapter);
}
// T5這里build一個Filter
return webSecurity.build();
}
- T1,T2,T3,T4處的代碼請查看代碼的注釋雨膨,這里重點講解T5處的代碼
-
webSecurity
對象在此時已經(jīng)加載完所有的配置 -
webSecurity
對象為我們創(chuàng)建一個Filter通過的是build()方法
1.2 WebSecurity
的build()
方法
WebSecurity
繼承了AbstractConfiguredSecurityBuilder
類擂涛,實現(xiàn)了SecurityBuilder
接口.事實上AbstractConfiguredSecurityBuilder
類也是實現(xiàn)了SecurityBuilder
接口。子類和父類實現(xiàn)同一個接口聊记。事實上是為了子類在反射調(diào)用方法getInterfaces()
中可以獲取到接口撒妈,根據(jù)這里的情況就是WebSecurity
反射調(diào)用getInterfaces()
可以獲取到SecurityBuilder
接口(個人理解,也許編寫者是另有深意)排监。-
WebSecurity
的繼承關系狰右,了解繼承關系,對于后面的代碼的理解大有好處
WebSecurity類圖 SecurityBuilder
定義了構(gòu)建的接口標準AbstractSecurityBuilder
實現(xiàn)build方法,用AtomicBoolean的變量building保證多線程情況下舆床,操作的原子性棋蚌。此處采用的是模板模式。定義了doBuild()抽象方法AbstractConfiguredSecurityBuilder
繼承AbstractSecurityBuilder
實現(xiàn)doBuild()方法,也采用模板模式挨队,定義了實現(xiàn)的具體的步驟谷暮,如UNBUILT
,INITIALIZING
,CONFIGURING
,BUILDING
瞒瘸,以及BUILT
1.2.1 SecurityBuilder
接口定義
public interface SecurityBuilder<O> {
/**
*
* Builds the object and returns it or null.
* @return the Object to be built or null if the implementation allows it.
* @throws Exception
*
*/
O build() throws Exception;
}
- 該接口的作用是定義一個構(gòu)建的對象標準
- 只定義了
build()
方法,返回值是一個泛型 - 我們看一下
WebSecurity
的定義
public final class WebSecurity extends
AbstractConfiguredSecurityBuilder<Filter, WebSecurity> implements
SecurityBuilder<Filter>, ApplicationContextAware {
...
...
}
- 從上面的代碼可以看出
WebSecurity
指定泛型的類型為Filter
坷备,結(jié)合上面接口build()
方法我們可以知道,WebSecurity
的build()
方法返回的是一個Filter
,Spring Securiy 通過這個來創(chuàng)建一個過濾器
1.2.2 WebSecurity
的build()
過程
- 從上面
WebSecurity
的類圖來看情臭,AbstractSecurityBuilder
保證了線程的安全省撑,AbstractConfiguredSecurityBuilder
保證了構(gòu)建過程以及構(gòu)建狀態(tài)。WebSecurity
通過performBuild()來實現(xiàn)自身的構(gòu)建 -
AbstractConfiguredSecurityBuilder
的構(gòu)建過程俯在,我們看一下doBuild()方法的定義
@Override
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = BuildState.INITIALIZING;
beforeInit(); //默認什么都沒做竟秫,WebSecurity也沒有重寫
init(); //T1 默認此處調(diào)用WebSecurityConfigurerAdapter的init(final WebSecurity web)方法
buildState = BuildState.CONFIGURING;
beforeConfigure(); //默認什么都不做,WebSecurity沒有重寫
configure();//調(diào)用WebSecurityConfigurerAdapter的configure(WebSecurity web),但是什么都沒做
buildState = BuildState.BUILDING;
O result = performBuild(); //T2這里調(diào)用WebSecurity的performBuild()方法
buildState = BuildState.BUILT;
return result; //從WebSecurity的實現(xiàn)跷乐,這里返回了一個Filter肥败,完成構(gòu)建過程
}
}
- T1處調(diào)用WebSecurityConfigurerAdapter的init(final WebSecurity web)方法,看一下源代碼的定義
/**
* @param web
* @throws Exception
*/
public void init(final WebSecurity web) throws Exception {
//構(gòu)建HttpSecurity對象
final HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
public void run() {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
}
});
}
這里構(gòu)建了HttpSecurity對象,以及有一個共享對象FilterSecurityInterceptor 馒稍,我們還是要將完整的流程講完皿哨,在下一篇文章對HttpSecurity對象做專門的解讀,因為這個HttpSecurity對象扮演者非常重要的角色
- T2 纽谒, 根據(jù)WebSecurity的屬性構(gòu)建Filter的performBuild()方法
@Override
protected Filter performBuild() throws Exception {
Assert.state(
//T1 securityFilterChainBuilders哪里來的?
!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");
//T2 ignoredRequests.size()到底是什么证膨?
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
//這個securityFilterChains 的集合里面存放的就是我們所有的過濾器鏈,根據(jù)長度的定義鼓黔,我們也可以知道分為兩種一個是通過ignoredRequests來的過濾器鏈,一個是通過securityFilterChainBuilders這個過濾器鏈構(gòu)建鏈來的央勒。
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(
chainSize);
//如果是ignoredRequest類型的,那么久添加默認過濾器鏈(DefaultSecurityFilterChain)
for (RequestMatcher ignoredRequest : ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
//如果是securityFilterChainBuilder類型的澳化,那么通過securityFilterChainBuilder的build()方法來構(gòu)建過濾器鏈
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
//將過濾器鏈交給一個過濾器鏈代理對象,而這個代理對象就是返回回去的過濾器,后面的代碼先不做交代崔步。到這里為止,過濾器的過程已經(jīng)結(jié)束
//T3 什么是FilterChainProxy?
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;
}
從上面源碼和標注的注釋(也許理解不透徹)來看,其實我們只要回答清楚上面的T1缎谷,T2井濒,T3這三個問題,就基本弄清楚了慎陵。
1.2.3 WebSecurity
的securityFilterChainBuilders
屬性哪里來的
- 我們在上面說到
AbstractConfiguredSecurityBuilder
的doBuild()
里面的init()方法實際上調(diào)用的是WebSecurityConfigurerAdapter的init(final WebSecurity web)方法眼虱,前面沒有詳細解釋,這里再次引入源碼
/**
* @param web
* @throws Exception
*/
public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();
//T1
web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
public void run() {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
}
});
}
- T1處調(diào)用了
WebSecurity
的addSecurityFilterChainBuilder()
方法席纽,我們看一下這個方法的源代碼
public WebSecurity addSecurityFilterChainBuilder(
SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder) {
//這個就是securityFilterChainBuilders變量
this.securityFilterChainBuilders.add(securityFilterChainBuilder);
return this;
}
- 從上面兩處代碼中我們似乎已經(jīng)知道捏悬,在構(gòu)建Filter過程的初始化的時候,我們對securityFilterChainBuilders這個變量進行了賦值润梯,默認情況下securityFilterChainBuilders里面只有一個對象过牙,那就是
HttpSecurity
1.2.4 ignoredRequests到底是什么
- 其實這個非常簡單ignoredRequests只是
WebSecurity
的一個屬性 - ignoredRequests的list中的值從哪里來的呢,其實我們可以看到里面有一個ignore()方法纺铭,通過這個來進行設置的
- 怎么設置呢寇钉,那我們得看看WebSecurityConfigurerAdapter這個類了,里面有一個configure方法供我們重寫
public void configure(WebSecurity web) throws Exception {
}
- 具體的例子如下
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
web.ignoring()
.mvcMatchers("/favicon.ico", "/webjars/**", "/css/**");
}
- 然后值得一提的是這里有多少個
mvcMatchers
就會創(chuàng)建多少個ignoredRequests
的對象舶赔,也就會有多少個過濾器鏈扫倡,至于源碼,可以自行閱讀竟纳,也是在WebSecurity
里面定義的內(nèi)部類IgnoredRequestConfigurer
這個類里面
1.2.5 FilterChainProxy
到底是什么
- 上面的描述中我們知道撵溃,
FilterChainProxy
是真正返回的Filter,上面代碼中FilterChainProxy
的對象創(chuàng)建的源碼為:
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
- 然后這個FilterChainProxy又是比較復雜的玩意兒锥累,我們還是在接下來文章繼續(xù)吧缘挑。
1.3 過濾器實現(xiàn)的流程
- 雖然到目前為止,整個還是沒有交代清楚桶略,但是我們還是需要畫一個小圖來整理一下前面的流程吧语淘。為接下來
HttpSecurity
和FilterChainProxy
類解讀做點準備
我們暫時的放過一下FilterChainProxy
诲宇,下一篇我們繼續(xù)HttpSecurity