天天看點

(翻譯)Spring Security-2.0.x參考文檔”技術概述“

技術概述

5.1. 運作環境

Spring Security可以運作在标準的Java 1.4運作環境下。 它也支援Java 5.0,不過這部分代碼單獨打包起來,放到釋出的,檔案名是"tiger"字首的JAR檔案裡。 因為Spring Security的目标是自己容器内管理,是以不需要為你的Java運作環境進行什麼特别的配置。 特别是,不需要特别配置一個Java Authentication and Authorization Service (JAAS)政策檔案,也不需要把Spring Security放到server的classLoader下。

上面這些設計,確定了釋出時的最大輕便性,你可以簡單把你的目标檔案(JAR或WAR或EAR)從一個系統複制到另一個系統,它會立即正常工作。

5.2. 共享元件

讓我們展示一些Spring Security中很重要的共享元件。 被成為"shared"的元件,是指它在架構中占有很重要的位置,架構離開它們就沒法運作。 這些java類表達了維持系統的建構代碼塊,是以了解他們的位置是非常重要的,即使你不需要直接跟他們打交道。

5.2.1. SecurityContextHolder, SecurityContext 和 Authentication對象

最基礎的對象就是SecurityContextHolder。 我們把目前應用程式的目前安全環境的細節存儲到它裡邊了。 預設情況下,SecurityContextHolder使用ThreadLocal存儲這些資訊,這意味着,安全環境在同一個線程執行的方法一直是有效的,即使這個安全環境沒有作為一個方法參數傳遞到那些方法裡。 這種情況下使用ThreadLocal是非常安全的,隻要記得在處理完目前主體的請求以後,把這個線程清除就行了。 當然,Spring Security自動幫你管理這一切了,你就不用擔心什麼了。

有些程式并不适合使用ThreadLocal,因為它們處理線程的特殊方法。 比如,swing用戶端也許希望JVM裡的usoyou線程都使用同一個安全環境。 為了這種情況,我們而已使用SecurityContextHolder.MODE_GLOBAL。 其他程式可能想讓一個線程建立的線程也使用相同的安全主體。 這時可以使用SecurityContextHolder.MODE_INHERITABLETHREADLOCAL。 想要修改預設的SecurityContextHolder.MODE_THREADLOCAL模式,可以使用兩種方法。 第一個是設定系統屬性。 另一個是調用SecurityContextHolder的靜态方法。 大多數程式不需要修改預設值,但是如果你需要做修改,先看一下SecurityContextHolder的JavaDoc中的詳細資訊。

我們把安全主體和系統互動的資訊都儲存在SecurityContextHolder中了。 Spring Security使用一個Authentication對應來表現這些資訊。 雖然你通常不需要自己建立一個Authentication對象,很常見的,使用者查詢Authentication對象。 你可以使用下面的代碼-在你程式中的任何位置-來做這件事:

Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

if (obj instanceof UserDetails) {

String username = ((UserDetails)obj).getUsername();

} else {

String username = obj.toString();

}

上面的代碼介紹了一定數量的,有趣的,幾個關鍵對象之間的互相關系。 首先,你會注意到SecurityContextHolder 和 Authentication之間的中間對象。 這個SecurityContextHolder.getContext()方法會直接傳回SecurityContext。

5.2.2. UserDetailsService

從上面的代碼片段中還可以看出另一件事,就是你可以從Authentication對象中獲得安全主體。 這個安全主體就是一個對象。 大多數情況下,可以強制轉換成UserDetails對象。 UserDetails是一個Spring Security的核心接口。 它代表一個主體,是擴充的,而且是為特定程式服務的。 想一下UserDetails章節,在你自己的使用者資料庫和如何把Spring Security需要的資料放到SecurityContextHolder裡。 為了讓你自己的使用者資料庫起作用,我們常常把UserDetails轉換成你系統提供的類,這樣你就可以直接調用業務相關的方法了(比如getEmail(), getEmployeeNumber()等等)。

現在,你可能想知道,我應該什麼時候提供這個UserDetails對象呢? 我怎麼做呢? 我想你說這個東西是聲明式的,我不需要寫任何代碼,怎麼辦? 簡單的回答是,這裡有一個特殊的接口,叫UserDetailsService。 這個接口裡的唯一一個方法,接收String類型的使用者名參數,傳回UserDetails。 大多數驗證提供器使用Spring Security代理UserDetailsService,作為驗證過程的一部分。 這個UserDetailsService用來建立Authentication對象,儲存在SecurityContextHolder裡。 好消息是我們提供了好幾個UserDetailsService實作,其中一個使用記憶體裡的map,另一個使用JDBC。 雖然,大多數使用者傾向于寫自己的,使用這些實作常常放到已有的資料通路對象(DAO)上,表示它們的雇員,客戶或其他企業應用中的使用者。 記住這個優勢,無論你用什麼UserDetailsService傳回的資料都可以通過SecurityContextHolder獲得,就像上面的代碼片段講的一樣。

5.2.3. GrantedAuthority

除了主體,另一個Authentication提供的重要方法是getAuthorities()。 這個方法提供了GrantedAuthority對象數組。 毫無疑問,GrantedAuthority是賦予到主體的權限。 這些權限通常使用角色表示,比如ROLE_ADMINISTRATOR 或 ROLE_HR_SUPERVISOR。 這些角色會在後面,對web驗證,方法驗證和領域對象驗證進行配置。 Spring Security的其他部分用來攔截這些權限,期望他們被表現出現。 GrantedAuthority對象通常使用UserDetailsService讀取的。

通常情況下,GrantedAuthority對象是應用程式範圍下的授權。 它們不會特意配置設定給一個特定的領域對象。 是以,你不能設定一個GrantedAuthority,讓它有權限展示編号54的Employee對象,因為如果有成千上網的這種授權,你會很快用光記憶體(或者,至少,導緻程式花費大量時間去驗證一個使用者)。 當然,Spring Security被明确設計成處理常見的需求,但是你最好别因為這個目的使用項目領域模型安全功能。

最後,但不是最不重要的,優勢你需要在HTTP請求之間共享SecurityContext. 其他時候,主體會在每個請求裡重新驗證,雖然大多數情況下它可以存儲。 HttpSessionContextIntegrationFilter就是實作在HTTP請求之間存儲SecurityContext的。 就像它的類名一樣,HttpSession被用來儲存這些資訊。 你不應該因為安全的問題,直接與HttpSession打交道。 根本不存在這樣做的理由-一直使用SecurityContextHolder作替代方式。

5.2.4. 小結

簡單回顧一下,Spring Security主要是由一下幾部分組成的:

*

SecurityContextHolder,提供幾種通路SecurityContext的方式。

*

SecurityContext,儲存Authentication資訊,和請求對應的安全資訊。

*

HttpSessionContextIntegrationFilter,為了在不同請求使用,把SecurityContext儲存到 HttpSession裡。

*

Authentication,展示Spring Security特定的主體。

*

GrantedAuthority,反應,在應用程式範圍你,賦予主體的權限。

*

UserDetails,通過你的應用DAO,提供必要的資訊,建構Authentication對象。

*

UserDetailsService,建立一個 UserDetails,傳遞一個 String類型的使用者名(或者證書ID或其他)。

現在,你應該對這種重複使用的元件有一些了解了。 讓我們貼近看一下驗證的過程。

5.3. 驗證

就像這篇指南開頭提到的那樣,Spring Security可在很多不同的驗證環境下使用。 雖然我們推薦人們使用Spring Security,不與已存在的容器管理認證系統整合,但它也是支援的-使用你自己的屬性驗證系統進行整合。 讓我們先看看Spring Security完全依靠自己,管理web安全,這裡會示範最複雜和最常見的情況。

讨論一個典型的web應用驗證過程:

1.

你通路首頁,點選一個連結。

2.

向伺服器發送一個請求,伺服器判斷你是否在通路一個受保護的資源。

3.

如果你還沒有進行過認證,伺服器發回一個響應,提示你必須進行認證。 響應可能是HTTP響應代碼,或者是重新定向到一個特定的web頁面。

4.

依據驗證機制,你的浏覽器将重定向到特定的web頁面,這樣你可以添加表單, 或者浏覽器使用其他方式校驗你的身份(比如,一個基本校驗對話框,cookie,或者X509證書,或者其他)。

5.

浏覽器會發回一個響應給伺服器。 這将是HTTP POST包含你填寫的表單内容,或者是HTTP頭部,包含你的驗證資訊。

6.

下一步,伺服器會判斷目前的證書是否是有效的, 如果他們是有效的,下一步會執行。 如果他們是非法的,通常你的浏覽器會再嘗試一次(是以你傳回的步驟二)。

7.

你發送的原始請求,會導緻重新嘗試驗證過程。 有希望的是,你會通過驗證,得到豬狗的授權,通路被保護的資源。 如果你有足夠的權限,請求會成功。否則,你會收到一個HTTP錯誤代碼403,意思是通路被拒絕。

Spring Security使用鮮明的類負責上面提到的每個步驟。 主要的部分是(為了使用他們)是ExceptionTranslationFilter, 一個AuthenticationEntryPoint, 一個驗證機制,一個AuthenticationProvider。

5.3.1. ExceptionTranslationFilter

ExceptionTranslationFilter是一個Spring Security過濾器,用來檢測是否抛出了Spring Security異常。 這些異常會被AbstractSecurityInterceptor抛出,它主要用來提供驗證服務。 我們會在下一節讨論AbstractSecurityInterceptor,但是現在,我們隻需要知道,它是用來生成Java異常,和知道跟HTTP沒啥關系,或者如何驗證一個主體。 而ExceptionTranslationFilter提供這些服務,使用特點那個的響應,傳回錯誤代碼403(如果主題被驗證了,但是權限不足-在上邊的步驟七),或者啟動一個AuthenticationEntryPoint(如果主體沒有認證,然後我們需要進入步驟三)。

5.3.2. AuthenticationEntryPoint

AuthenticationEntryPoint對應中上面清單中的步驟三。 如你所想的,每個web應用程式都有預設的驗證政策(好的,這可以在Spring Security裡配置一切,但是讓我們現在保持簡單)。 每個主要驗證系統會有它自己的AuthenticationEntryPoint實作, 會執行動作,如同步驟三裡的描述一樣。

在你的浏覽器決定送出你的認證證書之後(使用HTTP表單發送或者是HTTP頭),伺服器部分需要有一些東西來“收集”這些驗證資訊。 現在我們到了上述的第六步。 在Spring Security裡,我們需要一個特定的名字,來描述從使用者代碼(通常是浏覽器)收集驗證資訊的功能,這個名字就是“驗證機制”。 在從使用者代碼哪裡收集了驗證細節之後,一個"Authentication 請求"對象會被AuthenticationProvider建立。

5.3.3. AuthenticationProvider

Spring Security認證過程的最後一個角色是AuthenticationProvider。 非常簡單,這是跟獲得Authentication請求對象相關的,決定它是否是有效的。 這個供應器或者抛出一個異常,或者傳回一個完整的Authentication對象。 還記得我們的好朋友UserDetails和UserDetailsService嗎? 如果不記得,回頭看看前面的章節,重新整理你的記憶。 大多數AuthenticationProvider都會要求UserDetailsService提供一個UserDetails對象。 像上面提到的那樣,大多數程式會提供他們自己的UserDetailsService雖然一些可以使用Spring Security提供的JDBC和記憶體實作。 由此産生的UserDetails對象-特别是UserDetails中包含的GrantedAuthority[]-将被用來組裝Authentication對象。

在驗證機制重新獲得了組裝好的Authentication對象以後,他會認為請求有效,把Authentication放到SecurityContextHolder裡,然後導緻原始請求重審(第七步)。 另一方面,如果AuthenticationProvider駁回了請求,驗證機制會讓使用者代碼重試(第二步)。

5.3.4. 直接設定SecurityContextHolder的内容

雖然這表述了一個典型的驗證流程,但是好消息是Spring Security不在意你如何把一個Authentication放到SecurityContextHolder裡的。 唯一關鍵的需求是SecurityContextHolder包含Authentication,用來表現一個主體,在AbstractSecurityInterceptor之前驗證請求的。

你可以(很多使用者都這樣做)寫一個自己的過濾器或MVC控制器來提供驗證系統的互動,這些都不是基于Spring Security的。 比如,你也許使用容器管理驗證,從ThreadLocal或JNDI裡獲得目前使用者資訊。 或者,你的公司可能有一個遺留系統,它是一個企業标準,你不能控制它。 這種情況下,很容易讓Spring Security工作,也能提供驗證能力。 你所需要的就是寫一個過濾器(或等價物)從指定位置讀取第三方使用者資訊,把它放到SecurityContextHolder裡。 實作這些很簡單,這種整合是完全被支援的方法。

5.4. 安全對象

如果你熟悉AOP的話,就會知道有幾種不同的攔截方式:之前,之後,抛異常和環繞。 其中環繞是非常有用的,因為advisor可以決定是否執行這個方法,是否修改傳回的結果,是否抛出異常。 Spring Security為方法調用提供了一個環繞advice,就像web請求一樣。 我們使用AOP聯盟制作了一個方法調用的環繞advice,我們使用标準filter建立了對web請求的環繞advice。

對那些不熟悉AOP的人,需要了解的關鍵問題是Spring Security可以幫助你保護方法的調用,就像保護web請求一樣。 大多數人對保護服務層裡的安全方法非常按興趣。 這是因為在目前這一代J2EE程式裡,伺服器放了更多業務相關的邏輯(需要澄清,作者不建議這種設計方法,作為替代的,而是應該使用DTO,集會,門面和透明持久模式壓縮領域對象,但是貧血領域對象是目前的主流思路,我們會在這裡讨論它)。 如果你隻是需要保護服務層的方法調用,使用Spring标準AOP平台(被稱作AOP聯盟)就夠了。 如果你想直接保護領域對象,你會發現AspectJ非常值得考慮。

可以選擇使用AspectJ還是AOP聯盟處理方法驗證,或者你可以選擇使用filter處理web請求驗證。 你可以不選,選擇其中一個,選擇兩個,或者三個都選。 主流的應用是處理一些web請求驗證,再結合一些在服務層裡的AOP聯盟方法調用驗證。

Spring Security使用"secure object"來表示任何需要安全管理的對象。 Spring Security支援的每個安全對象都有它自己的類,他們都是AbstractSecurityInterceptor的子類。 重要的是,在AbstractSecurityInterceptor運作的時候,如果主體通過了認證,SecurityContextHolder裡就會包含一個合法的 Authentication。

AbstractSecurityInterceptor提供了一緻的流程,來處理安全方法的請求。 這個流程包括查找配置設定給目前請求的"配置屬性"。 這個"配置屬性"可以被想像成一個字元串,對AbstractSecurityInterceptor有特殊含義。 通常使用XML配置你的AbstractSecurityInterceptor。 無論如何,AbstractSecurityInterceptor會告訴AccessDecisionManager“這裡是配置屬性,這裡是目前Authentication對象,這裡是目前Authentication對象,這裡是目前請求的細節-然後,這個特定的主體,是否允許執行這個特定的操作嗎?”

假設AccessDecisionManager決定允許執行這個請求,AbstractSecurityInterceptor會正常執行這個請求。 話雖如此,罕見情況下,使用者可能需要把SecurityContext的Authentication換成另一個Authentication,通過通路RunAsManager。 這也許在,有原因,不常見的情況下有用,比如,服務層方法需要調用遠端系統,表現不同的身份。 因為Spring Security自動傳播安全身份,從一個伺服器到另一個(假設你使用了配置好的RMI或者HttpInvoker遠端調用協定用戶端),就可以用到它了。

按照下面安全對象執行和傳回的方式-可能意味着完全的方法調用或過濾器鍊的執行。 這種狀态下AbstractSecurityInterceptor對有可能修改傳回對象感興趣。 你可能想讓它發生,因為驗證決定不能“關于如何在”一個安全對象調用。 高可插拔性,AbstractSecurityInterceptor通過控制AfterInvocationManager,實際上在需要的時候,修改對象。 這裡類實際上可能替換對象,或者抛出異常,或者什麼也不做。

因為AbstractSecurityInterceptor是中央模闆類,更像第一時間為它服務。

關鍵"secure object"模型

Figure 5.1. 關鍵"secure object"模型

隻有開發者才會關心使用全心的方法,進行攔截和驗證請求,将直接使用安全方法。 比如,可能建立一個安全方法,控制對消息系統的權限。 安全需要的任何事情,也可以提供一種攔截的方法(好像AOP的環繞advice文法那樣)有可能在安全對象裡處理。 這樣說的話,大多數Spring應用簡單擁有三種目前支援的安全類型(AOP聯盟的MethodInvocation,AspectJ JoinPoint和web請求FilterInterceptor)完全透明的。

5.5. 結論

祝賀你! 你已經看過了足夠多的Spring Security的進階特性,着手于你的程式。 我們探讨了共享元件,驗證是如何工作的,并稽核了“安全對象”的正常驗證過程。 參考指南的後面部分,可能也可能滅有滿足你的特定要求,可以使用任何順序閱讀。

繼續閱讀