天天看點

慢慢來就很簡單的security學習(三)閱讀過濾鍊的建構源碼

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-支援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

  1. 它有一個注解 @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 容器
           
  1. AuthenticationConfiguration 這個類的功能是配置認證, 在下篇認證管理的部落格講, 簡述
該配置類主要是定義或者搜集建構AuthenticationManager的建構器AuthenticationManagerBuilder所需要的一些bean,然後設定到AuthenticationManagerBuilder用于最終AuthenticationManager對象的建構。

0x04 實際搞事情的類

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

的建立是由

WebSecurityConfiguration

類檢索了BeanFactory裡面所有

webSecurityConfigurer

類型的配置類, 并将這些配置類排序好, 裝載進被初始化好的

webSecurity

執行個體裡面, 然後執行

webSecurity#build()

方法建立的.

那麼問題來了, build() 做了些啥事情呢?

0x05 進行後面的講解, 可能需要知道的一個接口類

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

// 通過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() 到底做了些什麼事情?

先小結一下:

  1. 我們知道了 AbstractConfigurationSecurityBuilder<O, B extends SecurityBuilder<O.>>, 這是一個 SecurityBuilder<O.>
  2. 還有 WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity.>
  3. 還還有 SecurityFilterChain, FilterChainProxy
  4. 還還還有 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>
           
慢慢來就很簡單的security學習(三)閱讀過濾鍊的建構源碼
慢慢來就很簡單的security學習(三)閱讀過濾鍊的建構源碼

這是一個典型的建造者模式, 還有一個沒有介紹的 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 對于 <> 标簽會預設解析掉, 必須框在代碼片段裡面, 不然你們就看不到泛型了..
           

圖檔流程

慢慢來就很簡單的security學習(三)閱讀過濾鍊的建構源碼

參考資料

springboot情操陶冶-web配置(八)

Spring Security Config : 工具類 AutowireBeanFactoryObjectPostProcessor

Spring Security源碼解析(二.建立FilterChainProxy)

Spring Security Config : AuthenticationConfiguration

Spring Security源碼解析(一.基礎知識點與流程介紹)

Spring Security源碼解析(二.建立FilterChainProxy)

繼續閱讀