盤點 Spring Security 框架中的八大經典設計模式
上次有小夥伴建議,原始碼分析太枯燥了,要是能夠結合設計模式一起來,這樣更有助於大家理解 Spring Security 原始碼,同時還能複習一波設計模式。
因此松哥今天就試著整一篇,和大家來聊一聊 Spring Security 中涉及到的設計模式,不過 Spring Security 中涉及到的設計模式還是非常多的,松哥這裡講幾個,剩下的歡迎小夥伴們留言補充。
1.模板方法模式
Template Pattern(模板方法模式)是一個抽象類公開定義了執行它的方法的模板。它的子類可以按需要重寫方法實現,但呼叫將以抽象類中定義的方式進行,這是一種行為型模式。
模板方法方式優點如下:
在父類中提取了公共的部分程式碼,便於程式碼複用和擴充套件。 部分方法是由子類實現的,子類可以透過擴充套件方式增加相應的功能,符合開閉原則。
缺點如下:
對每個不同的實現都需要定義一個子類,導致類的個數增加,系統更加複雜,設計也更加抽象。 父類中的抽象方法由子類實現,子類執行的結果會影響父類的結果,增加了程式碼理解難度。
介紹完模板方法模式,大家可能大概猜到了 Spring Security 中哪些地方用到模板方法模式了。
我舉幾個簡單的例子。
第一個例子是 AbstractUserDetailsAuthenticationProvider 類的設計。大家都知道這個類是用來做驗證的,認證的邏輯在這個方法中都定義好了,但是該類卻定義了兩個抽象方法:
retrieveUser 該方法使用者從資料來源中獲取使用者物件。 additionalAuthenticationChecks 該方法用來做額外的校驗(登入憑證的校驗)
這兩個抽象方法是在 DaoAuthenticationProvider 中實現的。DaoAuthenticationProvider 的實現就是從資料庫中載入使用者,預設檢驗登入憑證也都是驗證密碼。
如果你的資料來源來自其他地方,或者登入憑證不是密碼,那麼自定義類繼承自 AbstractUserDetailsAuthenticationProvider 並重寫它裡邊的這兩個方法即可。
2.責任鏈模式
Chain of Responsibility Pattern(責任鏈模式) ,在這種模式中,通常每個接收者都包含對另一個接收者的引用,如果一個物件不能處理該請求,那麼它會把相同的請求傳給下一個接收者,依此類推。在這個過程中,客戶只需要將請求傳送到責任鏈上即可,無須關心請求的處理細節和請求的傳遞過程,所以責任鏈將請求的傳送者和請求的處理者解耦了。
責任鏈模式優點如下:
降低物件之間的耦合度。 增強了系統的可擴充套件性。 當工作流程發生變化,可以動態地改變鏈內的成員或者調動它們的次序。 簡化了物件之間的連線,每個物件只需保持一個指向其後繼者的引用,不需保持其他所有處理者的引用。 責任分擔,每個類只需要處理自己該處理的工作,符合類的單一職責原則。
缺點如下:
對比較長的職責鏈,請求的處理可能涉及多個處理物件,系統效能將受到一定影響。 職責鏈建立的合理性要靠客戶端來保證,增加了客戶端的複雜性。
很明顯,Spring Security 中的過濾器鏈就是一種責任鏈模式。一個請求到達後,被過濾器鏈中的過濾器逐個進行處理,過濾器鏈中的過濾器每個都具有不同的職能並且互不相擾,我們還可以透過 HttpSecurity 來動態配置過濾器鏈中的過濾器(即新增/刪除過濾器鏈中的過濾器)。
具體的程式碼在 FilterChainProxy$VirtualFilterChain 中,如下:
那麼接下來我們就來看看 VirtualFilterChain:
private static class VirtualFilterChain implements FilterChain {
private final FilterChain originalChain;
private final List<Filter> additionalFilters;
private final FirewalledRequest firewalledRequest;
private final int size;
private int currentPosition = 0;
private VirtualFilterChain(FirewalledRequest firewalledRequest,
FilterChain chain, List<Filter> additionalFilters) {
this.originalChain = chain;
this.additionalFilters = additionalFilters;
this.size = additionalFilters.size();
this.firewalledRequest = firewalledRequest;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if (currentPosition == size) {
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
+ " reached end of additional filter chain; proceeding with original chain");
}
// Deactivate path stripping as we exit the security filter chain
this.firewalledRequest.reset();
originalChain.doFilter(request, response);
}
else {
currentPosition++;
Filter nextFilter = additionalFilters.get(currentPosition - 1);
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
+ " at position " + currentPosition + " of " + size
+ " in additional filter chain; firing Filter: '"
+ nextFilter.getClass().getSimpleName() + "'");
}
nextFilter.doFilter(request, response, this);
}
}
}
VirtualFilterChain 類中首先宣告瞭 5 個全域性屬性,originalChain 表示原生的過濾器鏈,也就是 Web Filter;additionalFilters 表示 Spring Security 中的過濾器鏈;firewalledRequest 表示當前請求;size 表示過濾器鏈中過濾器的個數;currentPosition 則是過濾器鏈遍歷時候的下標。 doFilter 方法就是 Spring Security 中過濾器挨個執行的過程,如果 currentPosition == size
,表示過濾器鏈已經執行完畢,此時透過呼叫 originalChain.doFilter 進入到原生過濾鏈方法中,同時也退出了 Spring Security 過濾器鏈。否則就從 additionalFilters 取出 Spring Security 過濾器鏈中的一個個過濾器,挨個呼叫 doFilter 方法。nextFilter.doFilter 就是過濾器鏈挨個往下走。
關於 FilterChainProxy 的介紹,參見:深入理解 FilterChainProxy【原始碼篇】
3.策略模式
Strategy Pattern(策略模式),它定義了一系列演算法,將每一個演算法封裝起來,並讓它們可以相互替換。策略模式讓演算法獨立於使用它的客戶而變化,也稱為政策模式(Policy)。
策略模式的優點:
策略模式提供了對“開閉原則”的完美支援,使用者可以在不修改原有系統的基礎上選擇具體的策略,也可以靈活地擴充套件新的策略。 策略模式提供了管理相關的策略的方式。 策略模式提供了可以替換繼承關係的辦法。 使用策略模式可以避免使用多重條件轉移語句。
策略模式的缺點:
客戶端必須知道所有的策略類,並自行決定使用哪一個策略類。 策略模式將造成產生很多策略類(可以透過使用享元模式在一定程度上減少物件的數量)。
Spring Security 中使用策略模式的地方也有好幾個。
第一個就是使用者登入資訊儲存。
在 SecurityContextHolder 中定義登入使用者資訊儲存的方法,就定義了三種不同的策略:
public class SecurityContextHolder {
// ~ Static fields/initializers
// =====================================================================================
public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";
public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";
public static final String MODE_GLOBAL = "MODE_GLOBAL";
public static final String SYSTEM_PROPERTY = "spring.security.strategy";
private static String strategyName = System.getProperty(SYSTEM_PROPERTY);
private static SecurityContextHolderStrategy strategy;
}
使用者可以自行選擇使用哪一種策略!具體參見:在 Spring Security 中,我就想從子執行緒獲取使用者登入資訊,怎麼辦?
還有一個就是 session 併發管理。
在 AbstractAuthenticationProcessingFilter#doFilter 方法中,有如下程式碼:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
//省略
sessionStrategy.onAuthentication(authResult, request, response);
//省略
}
這就是一種策略模式。
Session 併發管理可以參考:
什麼是會話固定攻擊?Spring Boot 中要如何防禦會話固定攻擊? 叢集化部署,Spring Security 要如何處理 session 共享?
當然,這樣的例子還有很多,我就不一一列舉了。
4.代理模式
Proxy Pattern(代理模式) :給某一個物件提供一個代理,並由代理物件控制對原物件的引用,它是一種物件結構型模式。
代理模式的優點:
一定程度上降低了系統的耦合度。 代理物件可以擴充套件目標物件的功能。 代理物件可以保護目標物件。
缺點:
在客戶端和真實物件之間增加了代理,可能會導致請求的處理速度變慢。 增加了系統複雜度。
代理模式在 Spring Security 中最重要的應用就是 Spring Security 過濾器連結入 Web Filter 的過程,使用了 Spring 提供的 DelegatingFilterProxy,這就是一個典型的代理模式:
public class DelegatingFilterProxy extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// Lazily initialize the delegate if necessary.
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
synchronized (this.delegateMonitor) {
delegateToUse = this.delegate;
if (delegateToUse == null) {
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: " +
"no ContextLoaderListener or DispatcherServlet registered?");
}
delegateToUse = initDelegate(wac);
}
this.delegate = delegateToUse;
}
}
// Let the delegate perform the actual doFilter operation.
invokeDelegate(delegateToUse, request, response, filterChain);
}
}
當然還有其他很多地方也用到代理模式,我就不一一列舉了,歡迎小夥伴們留言補充。
5.介面卡模式
Adapter Pattern(介面卡模式),大家平時用的手機充電器學名叫做電源介面卡,它的作用是把 220V 的電壓轉為手機可用的 5V 電壓。所以介面卡模式其實也是類似作用,將一個介面轉換成客戶希望的另一個介面,介面卡模式使介面不相容的類可以一起工作。介面卡模式又分為類介面卡模式、物件介面卡模式以及介面介面卡模式。
介面卡模式的優點:
解耦,透過引入一個介面卡類來重用現有的適配者類,而無須修改原有程式碼。 增加了類的透明性和複用性。 具有較好的靈活性和擴充套件性都。
缺點:
由於 Java 不支援多重繼承,一次最多隻能適配一個適配者類,而且目標抽象類只能為抽象類,不能為具體類,其使用有一定的侷限性。
Spring Security 中的介面卡模式也是非常多的,例如我們最為常見的 WebSecurityConfigurerAdapter,該類讓兩個原本不相關的 WebSecurity 和 HttpSecurity 能夠在一起工作。
具體參見:深入理解 WebSecurityConfigurerAdapter【原始碼篇】
6.建造者模式
Builder Pattern(建造者模式)是將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的物件出來,使用者只需要指定複雜物件的型別和內容就可以構建物件,而不需要知道內部的具體構建細節。
建造者模式優點:
將產品本身與產品的建立過程解耦,使得相同的建立過程可以建立不同的產品物件,而客戶端不需要知道產品內部細節。 每一個產品對應一個建造者,使用者使用不同的建造者可以建立不同的產品,建造者本身可以輕鬆修改或者新增。 可以更加精細地控制產品的建立過程。
缺點:
建立的產品需要有一定的相似性,如果差異過大,則不適合建造者模式。 產品本身的複雜度會提高建造者的複雜度。
Spring Security 中對於建造者模式的使用也是非常多,例如典型的 AuthenticationManagerBuilder,它想要建造的物件是 AuthenticationManager,對應的建造方法則是 build。一般建造者模式中建造者類命名以 builder 結尾,而建造方法命名為 build()。
關於 AuthenticationManagerBuilder,參見:深入理解 AuthenticationManagerBuilder 【原始碼篇】 一文。
7.觀察者模式
Observer(觀察者模式)指多個物件間存在一對多的依賴關係,當一個物件的狀態發生改變時,所有依賴於它的物件都得到通知並自動更新,觀察者模式也稱為釋出-訂閱模式、模型-檢視模式,它是物件行為型模式。
觀察者模式優點:
降低了目標與觀察者之間的耦合關係,兩者之間是抽象耦合關係。
缺點:
目標與觀察者之間的依賴關係並沒有完全解除,而且有可能出現迴圈引用。 當觀察者物件很多時,程式執行效率降低。
在 Spring 框架中,觀察者模式用於實現 ApplicationContext 的事件處理功能。Spring 為我們提供了 ApplicationEvent 類和 ApplicationListener 介面來啟用事件處理。Spring 應用程式中的任何 Bean 實現 ApplicationListener 介面,都會接收到 ApplicationEvent 作為事件釋出者推送的訊息。在這裡,事件釋出者是主題(Subject) 和實現 ApplicationListener 的 Bean 的觀察者(Observer)。
具體到 Spring Security 中,如登入成功事件釋出,session 銷燬事件等等,都算是觀察者模式。
例如 AbstractAuthenticationProcessingFilter#successfulAuthentication 方法:
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
+ authResult);
}
SecurityContextHolder.getContext().setAuthentication(authResult);
rememberMeServices.loginSuccess(request, response, authResult);
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
authResult, this.getClass()));
}
successHandler.onAuthenticationSuccess(request, response, authResult);
}
類似還有很多,如 session 銷燬事件等(參見Spring Security 自動踢掉前一個登入使用者,一個配置搞定!),我這裡就不一一列舉了。
8.裝飾模式
Decorator(裝飾模式)是指在不改變現有物件結構的情況下,動態地給該物件增加一些額外功能的模式。
裝飾模式的優點:
可以靈活的擴充套件一個類的功能。
缺點:
增加了許多子類,使程式變得很複雜。
Spring Security 中對於裝飾模式也有許多應用。最典型的就是一個請求在透過過濾器鏈的時候會不停的變,會不停的調整它的功能,透過裝飾模式設計出了請求的許多類,例如:
HeaderWriterRequest FirewalledRequest StrictHttpFirewall SaveToSessionRequestWrapper ...
等等,類似的很多,我就不一一贅述了。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024922/viewspace-2938070/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Spring框架中的設計模式(二)Spring框架設計模式
- Spring框架中的設計模式(一)Spring框架設計模式
- .net core 中的經典設計模式的應用設計模式
- JavaScript設計模式經典之代理模式JavaScript設計模式
- JavaScript設計模式經典之策略模式JavaScript設計模式
- JavaScript設計模式經典之外觀模式JavaScript設計模式
- PHP三大經典設計模式PHP設計模式
- JavaScript設計模式經典之觀察者模式JavaScript設計模式
- JavaScript設計模式經典之單例模式JavaScript設計模式單例
- JavaScript設計模式經典之介面卡模式JavaScript設計模式
- 23個經典設計模式的Swift實現設計模式Swift
- 一個設計模式的經典學習case!!!設計模式
- 在Spring 當中存在的八大模式Spring模式
- 【經典案例】Python詳解設計模式:策略模式Python設計模式
- JavaScript設計模式經典之簡單工廠模式JavaScript設計模式
- Spring框架的設計理念與設計模式詳解Spring框架設計模式
- JDK中有關23個經典設計模式JDK設計模式
- Spring Security 安全框架Spring框架
- 設計模式 經典書籍必備推薦設計模式
- iPhone7八大驚豔設計盤點 全新女神紫色iPhone
- 設計模式(十五)——命令模式(Spring框架的JdbcTemplate原始碼分析)設計模式Spring框架JDBC原始碼
- Spring中如何使用設計模式Spring設計模式
- 詳解 PHP 中的三大經典模式PHP模式
- 設計模式(二十一)——直譯器模式(Spring 框架中SpelExpressionParser原始碼分析)設計模式Spring框架Express原始碼
- Spring Security框架中踢人下線技術探索Spring框架
- 經典程式設計引言程式設計
- 27款前端開發中經典的 CSS 框架前端CSS框架
- Java安全框架(一)Spring SecurityJava框架Spring
- Spring中的9種設計模式彙總Spring設計模式
- 索愛經典手機盤點 感受智慧時代的變遷
- Spring 經典教程Spring
- [收藏]Spring Security中的ACLSpring
- Spring Security 中的 BCryptPasswordEncoderSpring
- MVP模式的經典封裝MVP模式封裝
- JS設計模式入門和框架中的實踐JS設計模式框架
- JavaScript 設計模式入門和框架中的實踐JavaScript設計模式框架
- 【設計模式】漢堡中的設計模式——策略模式設計模式
- 27款經典的CSS框架分享CSS框架