Spring Security原理篇(二) 過濾器原理

上篇文章通過對WebSecurityConfiguration這個配置類的源碼閱讀,已經(jīng)了解到,在啟動的時候主要創(chuàng)建了兩個對象津辩,WebSecurity和名字為springSecurityFilterChainFilter。這篇文章主要是通過源碼閱讀,查看一下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 WebSecuritybuild()方法

  • 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)的具體的步驟谷暮,如UNBUILTINITIALIZING,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()方法我們可以知道,WebSecuritybuild()方法返回的是一個Filter,Spring Securiy 通過這個來創(chuàng)建一個過濾器

1.2.2 WebSecuritybuild()過程

  • 從上面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 WebSecuritysecurityFilterChainBuilders屬性哪里來的

  • 我們在上面說到AbstractConfiguredSecurityBuilderdoBuild()里面的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)用了WebSecurityaddSecurityFilterChainBuilder()方法席纽,我們看一下這個方法的源代碼
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)的流程

  • 雖然到目前為止,整個還是沒有交代清楚桶略,但是我們還是需要畫一個小圖來整理一下前面的流程吧语淘。為接下來HttpSecurityFilterChainProxy類解讀做點準備
Filter創(chuàng)建過程.png

我們暫時的放過一下FilterChainProxy诲宇,下一篇我們繼續(xù)HttpSecurity

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市惶翻,隨后出現(xiàn)的幾起案子姑蓝,更是在濱河造成了極大的恐慌,老刑警劉巖维贺,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件它掂,死亡現(xiàn)場離奇詭異,居然都是意外死亡溯泣,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門榕茧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來垃沦,“玉大人,你說我怎么就攤上這事用押≈荆” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵蜻拨,是天一觀的道長池充。 經(jīng)常有香客問我,道長缎讼,這世上最難降的妖魔是什么收夸? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮血崭,結(jié)果婚禮上卧惜,老公的妹妹穿的比我還像新娘。我一直安慰自己夹纫,他們只是感情好咽瓷,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著舰讹,像睡著了一般茅姜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上月匣,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天钻洒,我揣著相機與錄音,去河邊找鬼桶错。 笑死航唆,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的院刁。 我是一名探鬼主播糯钙,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了任岸?” 一聲冷哼從身側(cè)響起再榄,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎享潜,沒想到半個月后困鸥,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡剑按,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年疾就,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片艺蝴。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡猬腰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出猜敢,到底是詐尸還是另有隱情姑荷,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布缩擂,位于F島的核電站鼠冕,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏胯盯。R本人自食惡果不足惜懈费,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望陨闹。 院中可真熱鬧楞捂,春花似錦、人聲如沸趋厉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽君账。三九已至繁堡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間乡数,已是汗流浹背椭蹄。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留净赴,地道東北人绳矩。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像玖翅,于是被迫代替她去往敵國和親翼馆。 傳聞我的和親對象是個殘疾皇子割以,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 前言 最近在弄微信的OAuth認證,我們認證服務及作為客戶端又作為認證服務端应媚。因此需要對spring cloud ...
    不小下閱讀 11,964評論 3 54
  • 設計模式概述 在學習面向?qū)ο笃叽笤O計原則時需要注意以下幾點:a) 高內(nèi)聚严沥、低耦合和單一職能的“沖突”實際上,這兩者...
    彥幀閱讀 3,752評論 0 14
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理中姜,服務發(fā)現(xiàn)消玄,斷路器,智...
    卡卡羅2017閱讀 134,693評論 18 139
  • 剛走到這條小巷就看到了這位在學習的小男孩丢胚,靜悄悄地走過去翩瓜,偷偷摸摸地拍一張。 一個好可愛的小姑娘嗜桌,當我拿起手機要拍...
    懸樹閱讀 494評論 2 2
  • 自從懷孕奥溺,每天都在提心掉膽中度過,害怕自己的一個小習慣或者一個不經(jīng)意傷害到腹中的寶寶骨宠,卻也是在大大咧咧中度過,從不...
    蝶澈心涯閱讀 596評論 0 0