spring security 源碼絕對詳解(一)-過濾器鍊的建立
一切皆有法
0x00 廢話
源碼的閱讀是一件很痛苦的事情, 但也是一件很有趣的事情, 看大佬們建構的工業級的架構,
所獲得的提升也蠻大的.
畢竟源碼閱讀四舍五入約等于大神教你玩轉設計模式.
雖然源碼内容會引起不适, 但結合别人的部落格, 也不會很難, 反複反複反複.
so 打開idea 建立一個 security 項目, 開始跟代碼, do it~;
我使用的版本是 spring boot 1.5.6
0x01 從主security配置類的注解開始
@Configuration
@EnableWebSecurity
public class SecurityBrowserConfiguration extends WebSecurityConfigurerAdapter {
// do something
}
點進去
@EnableWebSecurity
可以看到
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
/**
* Controls debugging support for Spring Security. Default is false.
* @return if true, enables debug support with Spring Security
*/
// 開啟 debug 模式
boolean debug() default false;
}
而 @EnableGlobalAuthentication 長這個樣子
@Import(AuthenticationConfiguration.class)
@Configuration
public @interface EnableGlobalAuthentication {
}
是以可以知道
SpringWebMvcImportSelector.class
AuthenticationConfiguration.class
WebSecurityConfiguration.class
三個類是主要做事情的類. 看名字能知道這三個類的大概用途. 最終可以發現過濾器鍊在 WebSecurityConfiguration.class 裡建立.
// Creates the Spring Security Filter Chain
// DEFAULT_FILTER_NAME = 'springSecurityFilterChain' 也就是我們的目标
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
// do something...
}
直接從這個方法裡開始看, 雲裡霧裡. 知道了大概的流程, 但是始終不透徹. 是以我們慢慢慢慢慢來的, 把所有設計到的所有, 再都慢慢看一遍.
0x02 我們先看一下 SpringWebMvcImportSelector.class
SpringWebMvcImportSelector.class
/**
* SpringWebMvcImportSelector-支援mvc的參數安全校驗,替代了 @EnableWebMvcSecurity (已過時)注解.
* 如果在 classpath 環境中存在 mvc 的關鍵類 DispatcherServlet 時便會引入 WebMvcSecurityConfiguration 類
*/
class SpringWebMvcImportSelector implements ImportSelector {
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 判斷
boolean webmvcPresent = ClassUtils.isPresent(
"org.springframework.web.servlet.DispatcherServlet",
getClass().getClassLoader());
return webmvcPresent
? new String[] {
"org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration" }
: new String[] {};
}
}
在 mvc 環境下, 會導入 WebMvcSecurityConfiguration 這個類, 功能如下
/**
* WebMvcConfigurerAdapter接口主要是用于配置MVC的相關功能,比如參數處理器、傳回值處理器、異常處理器等等。
* 該實作類隻擴充了相應的參數處理器
* 也就是增加了 @AuthenticationPrincipal 注解, 可以用它來注解 Controller 層方法的參數
* 會自動從 SecurityContext 取值, 被注解的參數必須和存在 SecurityContext 的内容是同一類型
* (SecurityContext 在過濾器執行流程裡面有記載, 下次介紹, 現在把他當成一個容器就好了)
* 該注解主要是友善将校驗通過的 Token 用于參數指派, 還有一個 csrf 的參數解析(csrf 目前略掉)
*/
class WebMvcSecurityConfiguration extends WebMvcConfigurerAdapter implements ApplicationContextAware {
private BeanResolver beanResolver;
@Override
@SuppressWarnings("deprecation")
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
AuthenticationPrincipalArgumentResolver authenticationPrincipalResolver = new AuthenticationPrincipalArgumentResolver();
authenticationPrincipalResolver.setBeanResolver(beanResolver);
argumentResolvers.add(authenticationPrincipalResolver);
// 已過時
argumentResolvers.add(new org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver());
// // csrf token參數
argumentResolvers.add(new CsrfTokenArgumentResolver());
}
@Bean
public RequestDataValueProcessor requestDataValueProcessor() {
return new CsrfRequestDataValueProcessor();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.beanResolver = new BeanFactoryResolver(applicationContext.getAutowireCapableBeanFactory());
}
}
0x03 再看看 AuthenticationConfiguration.class
AuthenticationConfiguration.class
- 它有一個注解 @Import(ObjectPostProcessorConfiguration.class)
/**
* ObjectPostProcessorConfiguration 配置用于建立 AutowireBeanFactoryObjectPostProcessor 類
* AutowireBeanFactoryObjectPostProcessor 的作用
* Spring Security 的配置機制會使用到很多對象, 比如 WebSecurity, ProviderManager, 各個安全Filter等。
* 但對象的建立并不是通過bean定義的形式被容器發現和注冊進入容器的。而是 new 的.
* 但對于這些并未被容器管理的對象, Spring Security 也希望它們成為一個被容器管理的 bean
* 注入相應的依賴, 執行 applyBeanPostProcessorsAfterInitialization() 可以 afterSingletonsInstantiated, destroy
* 為達成這個目标,Spring Security配置機制提供了一個工具類AutowireBeanFactoryObjectPostProcessor
*/
@Configuration
public class ObjectPostProcessorConfiguration {
@Bean
public ObjectPostProcessor<Object> objectPostProcessor(AutowireCapableBeanFactory beanFactory) {
// 源碼很簡單, 限于篇幅就不貼了
return new AutowireBeanFactoryObjectPostProcessor(beanFactory);
}
}
// 例子
// objectObjectPostProcessor.postProcess(new DemoClass());
// 等于把 new DemoClass() 注入到了 IOC 容器
- AuthenticationConfiguration 這個類的功能是配置認證, 在下篇認證管理的部落格講, 簡述
該配置類主要是定義或者搜集建構AuthenticationManager的建構器AuthenticationManagerBuilder所需要的一些bean,然後設定到AuthenticationManagerBuilder用于最終AuthenticationManager對象的建構。
0x04 實際搞事情的類 WebSecurityConfiguration.class
WebSecurityConfiguration.class
@Configuration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
// 這也就是我們一開始說的建立過濾器鍊的方法
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = webSecurityConfigurers != null && !webSecurityConfigurers.isEmpty();
// 如果沒有配置類的話, 就導入一個預設的配置類
// 是以當我們隻在pom檔案導入security依賴的時候, 也會預設執行彈窗驗證的原因
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter =
objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter() {});
webSecurity.apply(adapter);
}
// WebSecurity#build()會傳回一個過濾器鍊
return webSecurity.build();
}
// 這個方法裡面設定和排序 webSecurityConfigurers
// WebSecurityConfigurerAdapter 繼承 WebSecurityConfigurer<WebSecurity>,
// 這裡就是在掃描我們寫的配置類, 并且按照 @Order 排序之後, 依次裝入WebSecurity裡面, 用于webSecurity.build();
// 看下面代碼知道, 我們的配置類可以寫很多個, 但是 @Order 設定的大小不能重複
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}")
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
// 建立并初始化 webSecurity
webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
if (debugEnabled != null) {
webSecurity.debug(debugEnabled);
}
// 使用 AnnotationAwareOrderComparator規則, 對所有的 webSecurityConfigurer 進行排序,
Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);
// 如果有兩個類排序值相同, 就報錯....
// 不知道為什麼這樣設計, 可能是過濾器鍊比對路徑的問題
Integer previousOrder = null;
Object previousConfig = null;
for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
if (previousOrder != null && previousOrder.equals(order)) {
throw new IllegalStateException(
"@Order on WebSecurityConfigurers must be unique. Order of "
+ order + " was already used on " + previousConfig + ", so it cannot be used on "
+ config + " too.");
}
previousOrder = order;
previousConfig = config;
}
// 依次将他加入到 webSecurity 裡面
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
}
// 設定 webSecurityConfigurers
this.webSecurityConfigurers = webSecurityConfigurers;
}
// 上面的方法裡參數設定值的時候用 spel 調用此方法, 将傳回值設定進參數裡面
@Bean
public AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
ConfigurableListableBeanFactory beanFactory) {
return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
}
}
// 這個類的作用就是發現所有 webSecurityConfigurer 配置類
final class AutowiredWebSecurityConfigurersIgnoreParents {
private final ConfigurableListableBeanFactory beanFactory;
public AutowiredWebSecurityConfigurersIgnoreParents(
ConfigurableListableBeanFactory beanFactory) {
Assert.notNull(beanFactory, "beanFactory cannot be null");
this.beanFactory = beanFactory;
}
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<>();
Map<String, WebSecurityConfigurer> beansOfType =
beanFactory.getBeansOfType(WebSecurityConfigurer.class);
for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}
}
我們目前可以知道的建立是由
springSecurityFilterChain
類檢索了BeanFactory裡面所有
WebSecurityConfiguration
類型的配置類, 并将這些配置類排序好, 裝載進被初始化好的
webSecurityConfigurer
執行個體裡面, 然後執行
webSecurity
webSecurity#build()
方法建立的.
那麼問題來了, build() 做了些啥事情呢?
0x05 進行後面的講解, 可能需要知道的一個接口類 SecurityBuilder
SecurityBuilder
/*
繼承樹如下 :
SecurityBuilder <O>
|-> AbstractSecurityBuilder<O>
|-> AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
|-> AuthenticationManagerBuilder 下次講
|-> HttpSecurity 注意這個, 應該不陌生吧
|-> WebSecurity 我們的目标啦
*/
// 看一下實際的代碼
public interface SecurityBuilder<O> {
/**
* Builds the object and returns it or null.
*/
O build() throws Exception;
}
// 實際作用可以看出來就是傳回一個 O 類型的`單例`對象
// 子類繼承實作 doBuild(), 做實際的 Build 邏輯
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
private AtomicBoolean building = new AtomicBoolean();
private O object;
public final O build() throws Exception {
if (this.building.compareAndSet(false, true)) {
this.object = doBuild();
return this.object;
}
throw new AlreadyBuiltException("This object has already been built");
}
public final O getObject() {
if (!this.building.get()) {
throw new IllegalStateException("This object has not been built");
}
return this.object;
}
protected abstract O doBuild() throws Exception;
}
/**
* 這個類有點複雜, 因為3個重點類都複用了它, 簡述如下
* 1. 允許将多個安全配置器 SecurityConfigurer 應用到該 SecurityBuilder 上;
* 2. 定義了建構過程的生命周期(參考生命周期狀态定義 BuildState );
* 3. 在生命周期基礎之上實作并 final 了基類定義的抽象方法 #doBuild, 将建構劃分為三個主要階段#init,#configure,#performBuild;
* 1. 對 #init/#configure階段提供了實作;
* 2. 對 #init/#configure階段提供了前置回調 #beforeInit/#beforeConfigure 空方法供基類擴充;
* 3. #performBuild 定義為抽象方法要求子類提供實作;
* 4. 登記安全建構器工作過程中需要共享使用的一些對象。
*
* @param <O> The object that this builder returns
* @param <B> The type of this builder (that is returned by the base class)
*/
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>> extends AbstractSecurityBuilder<O> {
// 包含了所要應用到目前 SecurityBuilder 上的所有的 SecurityConfigurer
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
// 用于記錄在初始化期間添加進來的 SecurityConfigurer
private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();
// 存放共享對象
private final Map<Class<? extends Object>, Object> sharedObjects = new HashMap<>();
// 對象後置處理器,也就是先前介紹的那個 new AutowireBeanFactoryObjectPostProcessor()
private ObjectPostProcessor<Object> objectPostProcessor;
// 生命周期狀态定義
private BuildState buildState = BuildState.UNBUILT;
// build() 保證對象是單例的
// doBuild() 控制建立對象的4個階段, 其中
// #init() 方法, 周遊 configurers 類裡面所有的配置對象, 并執行他們的 init(B builder) 方法
// #configure() 方法, 周遊 configurers 類裡面所有的配置對象, 并執行他們的 configure(B builder) 方法
// performBuild() 子類覆寫重寫, 用于實際控制 B.build() 傳回的對象
@Override
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = BuildState.INITIALIZING;
beforeInit(); // 留給子類拓展
init(); // 調用 configurers 裡面每一個 configurer 的 init 方法
buildState = BuildState.CONFIGURING;
beforeConfigure(); // 留給子類拓展
configure(); // 調用 configurers 裡面每一個 configurer 的 configure 方法
buildState = BuildState.BUILDING;
O result = performBuild(); // 子類提供實作
buildState = BuildState.BUILT;
return result;
}
}
// 應用一個 SecurityConfigurer 到該 SecurityBuilder 上
public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
add(configurer);
return configurer;
}
// 添加 SecurityConfigurer 到目前 SecurityBuilder 上,添加過程做了同步處理
private <C extends SecurityConfigurer<O, B>> void add(C configurer) throws Exception {
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<SecurityConfigurer<O, B>>(1);
}
configs.add(configurer);
this.configurers.put(clazz, configs);
if (buildState.isInitializing()) {
this.configurersAddedInInitializing.add(configurer);
}
}
}
}
0x06 還可能需要知道的一個接口類 SecurityConfigurer
/**
* 初始化B, 且配置B的相關屬性, 這句話能概括它的全部特性
* B SecurityBuilder<O>的子類
* O B.build()傳回的object類型
*/
public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
// 初始化 SecurityBuilder<O> 隻建立設定了共享的變量,不會設定 configure() 中需要的特殊屬性
void init(B builder) throws Exception;
// 設定SecurityBuilder<O>的特殊屬性
void configure(B builder) throws Exception;
}
/*
繼承樹如下 :
SecurityConfigurer<O, B extends SecurityBuilder<O>>
|-> WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends SecurityConfigurer<Filter, T> // 這是一個空接口
|-> WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> (是不是感覺終于見到老朋友了)
*/
// 注意看它的 init() 和 configure() 方法
public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> {
// 它作為 SecurityConfigurer 所具有的兩個功能
// 1, 隻設定了共享的變量 securityInterceptor
public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();
// 這個注冊的線程在 WebSecurity#performBuild() 裡面, 被調用, 但是是直接run()的, 而不是start().
// 用于設定 securityInterceptor
web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
public void run() {
FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
}
});
}
// 2, 可以配置需要忽略的請求
public void configure(WebSecurity web) throws Exception {
}
/**
* 其中 getHttp() 用于擷取HttpSecurity執行個體, 它長這個樣子
* 我們繼承這個類, 重寫了 configure(http) 方法的時候, 會在 httpSecurity.configurers 裡加入 filter
* http.csrf() 會将 CsrfConfigurer<HttpSecurity> 存入 HttpSecurity.configurers 中
* http.csrf().disable() 從 HttpSecurity.configurers 中移除 CsrfConfigurer
* 依次類推
*/
protected final HttpSecurity getHttp() throws Exception {
if (http != null) {
return http;
}
// 事件釋出器
DefaultAuthenticationEventPublisher eventPublisher =
objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
// 會調用 configure(AuthenticationManagerBuilder auth)
AuthenticationManager authenticationManager = authenticationManager();
authenticationBuilder.parentAuthenticationManager(authenticationManager);
// 共享對象
Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();
// 初始化 httpSecurity 對象
http = new HttpSecurity(objectPostProcessor, authenticationBuilder, sharedObjects);
if (!disableDefaults) {
// headers()等方法将configure apply()到了http的屬性configurers中,這裡預設會注入10個configurer
http.csrf().and()
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling().and()
.headers().and()
.sessionManagement().and()
.securityContext().and()
.requestCache().and()
.anonymous().and()
.servletApi().and()
.apply(new DefaultLoginPageConfigurer<HttpSecurity>()).and()
.logout();
ClassLoader classLoader = this.context.getClassLoader();
// 預設是為空
List<AbstractHttpConfigurer> defaultHttpConfigurers =
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
http.apply(configurer);
}
}
// this.configurer(http) 根據this的實作類選擇對應方法
configure(http);
return http;
}
}
// 子類一般繼承實作的3個configure()調用時間全都看到了
0x07 還還可能還需要知道的兩個東西 SecurityFilterChain
FilterChainProxy
SecurityFilterChain
FilterChainProxy
// 通過build()擷取到執行個體後,存入FilterChainProxy的屬性List<SecurityFilterChain> filterChains中,
// FilterChainProxy.doFilterInternal()中執行getFilters(HttpServletRequest request)
// 找出對應request的SecurityFilterChain并傳回Filters
public interface SecurityFilterChain {
// 1, 請求能否被比對
boolean matches(HttpServletRequest request);
// 2, 傳回此過濾器鍊的全部過濾器
List<Filter> getFilters();
}
// 本質上還是一個Filter, 這也是最終被反回的對象的類型
public class FilterChainProxy extends GenericFilterBean {
// 重要屬性,由WebSecurity中的performBuild()方法傳遞值過來
private List<SecurityFilterChain> filterChains;
// somethings
}
0x08 so… WebSecurity#build() 到底做了些什麼事情?
先小結一下:
- 我們知道了 AbstractConfigurationSecurityBuilder<O, B extends SecurityBuilder<O.>>, 這是一個 SecurityBuilder<O.>
- 還有 WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity.>
- 還還有 SecurityFilterChain, FilterChainProxy
- 還還還有 AbstractConfigurationSecurityBuilder 的兩個實作類
// 看這個聲明, 可以知道 webSecurity#build(), 可以作為一個 SecurityBuilder 建立一個 Filter
public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity>
implements SecurityBuilder<Filter>
// httpSecurity#build(), 可以建立一個 DefaultSecurityFilterChain
public final class HttpSecurity extends
AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
implements SecurityBuilder<DefaultSecurityFilterChain>,
HttpSecurityBuilder<HttpSecurity>

這是一個典型的建造者模式, 還有一個沒有介紹的 AuthenticationManagerBuilder(在AuthenticationConfiguration.class裡面).
WebConfiguration 控制 webSecurity 的建立, 而 webSecurity 又控制 httpSecurity 的建立過程.
FilterChainProxy由WebSeurity.Class負責建構,在WebSeurity.Class建構過程中,同時會對HttpSecurity進行建構,由HttpSecurity建構出内部的Filter攔截鍊
目前該知道的都知道了, 接下來詳細講 webSecurity#build() 是怎麼建立一個 filter 的
- 從 WebSecurityConfiguration 類的 setFilterChainProxySecurityConfigurer() 方法說起;
- 在參數上的 sqel 表達式, 收集到應用環境裡面所有的 WebSecurityConfigurer,
- 将他設定進 SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurers 參數裡面
- new 一個 webSecurity 對象, 并用 objectPostProcessor 處理, 讓他具有和被ioc持有的bean一樣的性質
- 将 webSecurityConfigurers 進行排序, 如果排序值相同就報錯
- 依次将 webSecurityConfigurers 裡面的每一個 WebSecurityConfigurer 設定進 WebSecurity 裡面;
- 将 WebSecurityConfigurer 設定進 WebSecurity 調用了它的 apply() 方法
- 也就是将 WebSecurityConfigurer 設定到 AbstractConfiguredSecurityBuilder 的 configurers 裡面
- configurers 的類型是 LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>>
- 最後将 WebSecurityConfiguration 類的 webSecurityConfigurers 屬性指派
- springSecurityFilterChain() 傳回一個 'springSecurityFilterChain'
- 檢視 WebSecurityConfiguration 類的 webSecurityConfigurers 是否有值
- 沒有的話, 就向 WebSecurity 裡 apply() 一個預設的 WebSecurityConfigurer
- 執行 webSecurity 的 build() 方法
- 執行 doBuild() 保證對象是單例的
- 執行 init() 方法
- 執行 configurers 裡每一個 configurer 的 init(B builder) 方法
- configurer#init(B builder) 方法傳入的就是在上面建立的WebSecurity對象
- WebSecurityConfigurerAdapter#init(WebSecurity web) 将會用 getHttp() 初始化一個 httpSecurity 對象
- 也就是 WebSecurityConfigurer 持有配置了一個 HttpSecurity 對象(上面建造者設計模式的圖)
- WebSecurityConfigurerAdapter 将各式各樣的 FilterConfigurer 注入到 HttpSecurity
- HttpSecurity 将 filter 用 FilterConfigurer 配置
- 然後把HttpSecurity注入到WebSecurity中
- 并把 httpSecurity 對象 設定進 web(也就是前面建立的webSecurity)的 securityFilterChainBuilders 屬性裡面
- 擷取 httpSecurity 裡 FilterSecurityInterceptor 類型的共享對象, 設定進webSecurity裡面
- 執行 configure() 方法
- 執行 configurers 裡每一個 configurer 的 configure(B builder) 方法
- WebSecurityConfigurerAdapter#configure(WebSecurity web) 預設空方法, 一般用于設定需要忽略的請求
- 也就是往 ignoredRequests 裡面加 RequestMatcher
- 執行 performBuild() 方法
- 建立一個 securityFilterChains, 類型是 List<SecurityFilterChain>
- 周遊 webSecurity 的 ignoredRequests, 建立比對不處理路徑的 SecurityFilterChain, 将他加入 securityFilterChains
- 周遊 securityFilterChainBuilders(也就是所有httpSecurity對象), 執行httpSecurit的build(), 将傳回的值加入 securityFilterChains
- httpSecurity#build(), 這個方法傳回一個過濾器鍊 DefaultSecurityFilterChain(requestMatcher, filters)
- requestMatcher 是比對規則, 預設的 AnyRequestMatcher.INSTANCE;
- filters 是一組利用http.and()加入的過濾器 List<Filter> filters = new ArrayList<Filter>();
- 用 securityFilterChains 建立一個 FilterChainProxy 對象
- 設定 filterChainProxy 的一些屬性, 轉型為 Filter, 并将它傳回 (這也就是我們的目标 'springSecurityFilterChain' )
// 吐槽下 md 對于 <> 标簽會預設解析掉, 必須框在代碼片段裡面, 不然你們就看不到泛型了..
圖檔流程
參考資料
springboot情操陶冶-web配置(八)
Spring Security Config : 工具類 AutowireBeanFactoryObjectPostProcessor
Spring Security源碼解析(二.建立FilterChainProxy)
Spring Security Config : AuthenticationConfiguration
Spring Security源碼解析(一.基礎知識點與流程介紹)
Spring Security源碼解析(二.建立FilterChainProxy)