他總是自動彈出來 在那么回事(它會自動彈出來)
1029
2025-03-31
本篇摘自胖哥最新的基于Spring Security 5.6.x的《Spring Security干貨》教程。舊版的教程將在2022年1月1日下線,請需要的同學盡快通過本公眾號回復“2021開工福利”下載。原創不易,請多多關注、、轉發。
Spring Security最難的地方就是HttpSecurity的頂層設計。不信你看看HttpSecurity的定義。
public?final?class?HttpSecurity?extends?AbstractConfiguredSecurityBuilder
感覺不到的話,再給你看看UML圖:
為什么要這么復雜?我第一次看到HttpSecurity的結構時我懷疑我自己是不是Java開發。多年以后,當我深入學習了之后才理解了這種設計。作為一個框架,尤其是安全框架,配置必須足夠靈活才能適用于更多的業務場景。Spring Security采取了配置與構建分離的架構設計來保證這一點。
配置與構建分離
配置只需要去收集配置項,構建只需要把所有的配置構建成目標對象。各干各的,分離職責,這種做法能夠提高代碼的可維護性和可讀寫性。Spring Security利用接口隔離把配置和構建進行高度抽象,提高靈活度,降低復雜度。不過這個體系依然非常龐大。為了降低學習難度需要把大問題拆解成小問題,各個擊破,這種學習方法在學習一些復雜的抽象理論時很湊效。
SecurityBuilder
SecurityBuilder就是對構建的抽象。你看上面的類圖過于復雜,而看SecurityBuilder就非常的簡單了。
public?interface?SecurityBuilder
就一個動作,構建泛化的目標對象O。通過下面這一組抽象和具體的定義我想你應該明白SecurityBuilder了吧。
//?抽象 ?SecurityBuilder?->?O ?//?具體 ?HttpSecurity->DefaultSecurityFilterChain
一句話,構建的活都是我來干。
AbstractSecurityBuilder
AbstractSecurityBuilder是對SecurityBuilder的實現。源碼如下:
public?abstract?class?AbstractSecurityBuilder
它通過原子類AtomicBoolean對構建方法build()進行了調用限制:每個目標對象只能被構建一次,避免安全策略發生不一致的情況。構建方法還加了final關鍵字,不可覆寫!構建的核心邏輯通過預留的鉤子方法doBuild()來擴展,鉤子方法是很常見的一種繼承策略。另外AbstractSecurityBuilder還提供了獲取已構建目標對象的方法getObject。
一句話,構建的活我只干一次。
HttpSecurityBuilder
public?interface?HttpSecurityBuilder
HttpSecurityBuilder對DefaultSecurityFilterChain的構建進行了增強,為其構建器增加了一些額外的獲取配置或管理配置的入口,參見上面的注釋。補充一點這個接口最大的功能就是打通了構建和配置的關系,可以操作下面要講的SecurityConfigurer。
一句話,我只構建DefaultSecurityFilterChain。
SecurityConfigurer是對配置的抽象。配置只是手段,構建才是目的。因此配置是對構建的配置。
public?interface?SecurityConfigurer
SecurityConfigurer有兩個方法,都非常重要。一個是init方法,這個方法你可以認為是SecurityBuilder構造函數的邏輯。如果你想在SecurityBuilder初始化的時候執行一些邏輯或者在后續配置中共享一些變量的話就可以在init方法中去實現;第二個方法是configure,為SecurityBuilder配置一些必要的屬性。到這里還沒完?這兩個方法有著明確的先后執行順序。在一次構建內可能有多個SecurityConfigurer,只有全部的init逐個執行完畢后才會逐個執行configure方法。相關的源碼在AbstractConfiguredSecurityBuilder中的標記部分:
@Override ?protected?final?O?doBuild()?throws?Exception?{ ??synchronized?(this.configurers)?{ ???this.buildState?=?BuildState.INITIALIZING; ???beforeInit(); ????????????//?①?執行所有的初始化方法 ???init(); ???this.buildState?=?BuildState.CONFIGURING; ???beforeConfigure(); ????????????//?②?執行所有的configure方法 ???configure(); ???this.buildState?=?BuildState.BUILDING; ???O?result?=?performBuild(); ???this.buildState?=?BuildState.BUILT; ???return?result; ??} ?}
一句話,配置SecurityBuilder的事都是我來干。
SecurityConfigurerAdapter
SecurityConfigurer在某些場景下是有局限性的,它不能獲取正在配置的SecurityBuilder,因此你無法進一步操作SecurityBuilder,配置的擴展性將大打折扣。因此引入了SecurityConfigurerAdapter來擴展SecurityConfigurer。
public?abstract?class?SecurityConfigurerAdapter
這樣可以指定SecurityBuilder,而且可以把SecurityBuilder暴露出來,隨時隨地去調整SecurityBuilder,靈活性大大提高。具體說的話,你可以通過and()方法獲取SecurityBuilder并對SecurityBuilder的其它配置項進行操作,比如上圖中SecurityConfigurerAdapter之間的切換。除此之外還引入了ObjectPostProcessor來后置操作一些并不開放的內置對象。關于ObjectPostProcessor會找個合適的場景去講解它。
一句話,配置SecurityBuilder不算什么,靈活適配才是花活。
AbstractHttpConfigurer
不是所有的配置都是有用的,有些配置我們希望有個關閉的入口功能。比如csrf功能,控制著csrf的配置的是CsrfConfigurer,如果CsrfConfigurer有一個關閉功能就好了。因此從SecurityConfigurerAdapter衍生出AbstractHttpConfigurer來滿足這個需求。
public?abstract?class?AbstractHttpConfigurer
AbstractHttpConfigurer的實現類非常多,日常的配置項大都由AbstractHttpConfigurer的實現類來控制。這個類是做定制化配置的一個重要入口之一,如果你想精通Spring Security,這個類一定要掌握。
一句話,我能“殺”我自己。
AbstractConfiguredSecurityBuilder
我們希望有多個SecurityConfigurer配置SecurityBuilder,表單登錄的、會話管理、csrf等等。用到什么配置什么,讓配置基于策略。因此引入了AbstractConfiguredSecurityBuilder。
public?
通過上面兩個apply方法就可以把所有的SecurityConfigurer適配進來,然后通過doBuilder進行精細化構建生命周期。你可以在各個生命周期階段進行一些必要的操作。
一句話,所有的配置都由我來進行適配。
總結
我們把Spring Security整個配置構建體系拆分了來看會簡單的多一些。即使這樣想理解這個體系也絕非靠一篇兩篇文章也是不現實的。不過從中也可以看得出一個道理,如果你的代碼想高度靈活,就必須把各個生命周期分層地高度抽象才行。
2021版Spring Security實戰干貨教程即將下線,2022版即將上線!
2021-12-15
更快的Maven來了
2021-12-23
能進這個Java組織的都是大神,現在只有三個中國人
2021-12-22
如果你是頭條用戶,一定要加入這個程序員組織
2021-12-22
前瞻|Spring 6.0將停止支持Freemarker和JSP
2021-12-20
Spring
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。