天天看點

shiro實戰系列(七)之Realm

Realm 是一個能夠通路應用程式特定的安全資料(如使用者、角色及權限)的元件。Realm 将應用程式特定的資料轉 換成一種 Shiro 能夠了解的格式,這樣 Shiro 能夠提供一個單一的易了解的 Subject 程式設計 API,無論有多少資料源存在 或你應用程式特定的資料是怎樣的。   Realm 通常和資料源是一對一的對應關系,如關系資料庫,LDAP 目錄,檔案系統,或其他類似資源。是以,Realm 接口的實作使用資料源特定的 API 來展示授權資料(角色,權限等),如 JDBC,檔案 IO,Hibernate 或 JPA,或其他 資料通路 API。

Realm 實質上就是一個特定安全的 DAO   因為這些資料源大多通常存儲身份驗證資料(如密碼的憑證)以及授權資料(如角色或權限),每個 Shiro Realm 能夠執行身份驗證和授權操作。

(1)Realm configuration

如果使用 Shiro 的 INI 配置檔案,你能夠自定義及引用 Realm,就像在[main]項中的任何其他對象一樣,但它們在 securityManager 中采用兩種方法之一進行配置:顯式或隐式。

(2)Explicit Assignment

基于迄今的 INI 配置知識,這是一個顯示的配置方法。在定義一個或多個 Realm 後,你将它們作為 securityManager 對象的集合屬性

shiro實戰系列(七)之Realm

顯式配置設定是确定的——你控制具體使用哪一個 Realm 及它們用于身份驗證和授權的順序。Realm 順序的作用在 Authentication 章的 Authentication Sequence 節進行了詳細的介紹。

(3) Implicit Assignment

Not Preferred(不推薦)         

這種方法可能引發意想不到的行為,如果你改變 realm 定義的順序的話。建議你避免使用此方法,并使用顯式配置設定, 它擁有确定的行為。該功能很可能在未來的 Shiro 版本中被廢棄或移除。   如果出于某些原因你不想顯式地配置 securityManager.realms 的屬性,你可以允許 Shiro 檢測所有配置好的 realm 并直接将它們指派給 securityManager。   使用這種方法,realm 将會按照它們預先定義好的順序來指派給 securityManager 執行個體。   也就是說,對于下面的 shiro.ini 示例:

blahRealm = com.company.blah.Realm              

fooRealm =  com.company.foo.Realm

barRealm = com.company.another.Realm

 # no securityManager.realms assignment here 

基本上和下面這一行具有相同的效果:

 securityManager.realms = $blahRealm, $fooRealm, $barRealm

然而,實作隐式配置設定,隻是 realm 定義的順序直接影響到了它們在身份驗證和授權嘗試中的通路順序。如果你改變 它們定義的順序,你将改變主要的 Authenticaor 的 Authentication Sequence 是如何起作用的。   由于這個原因,以及保證明确的行為,我們推薦使用顯式配置設定而不是隐式配置設定。 

(3) Realm Authentication

當你了解了 Shiro 的主要 Authentication 工作流後,了解在一個授權嘗試中當 Authenticator 與 Realm 互動時到底發 生了什麼是很重要的。

a.Supporting AuthenticationTokens

在 authentication sequence 中已經提到,當在 Realm 被通路來執行一個授權嘗試之前,它的supports 方法被調用。 如果傳回值為 true,則隻有這樣它的 getAuthenticationInfo(token)方法才會被調用。   通常 realm 會檢查送出的 token 的類型(接口或類)來判斷它是否能夠處理它。

例如,一個能夠處理生物資料的 realm 可能就一點也不了解 UsernamePasswordTokens,這樣它将從 supports 方法傳回 false。  

b.Handling supported AuthenticationTokens

若 Realm 支援一個送出的 AuthenticationToken,那麼 Authenticator 将會調用該 Realm 的 getAuthenticationInfo(token) 方法。這有效地代表了一個與 Realm 的後備資料源的授權嘗試。該方法按以下方法進行: 

 1. 為主要的識别資訊(帳戶識别資訊)檢查 token。

 2. 基于 principal 在資料源中尋找相吻合的帳戶資料。

3. 確定 token 支援的 credentials 比對那些存儲在資料源的。

4. 若 credentials 比對,傳回一個封裝了 Shiro 能夠了解的帳戶資料格式的 AuthenticationInfo 執行個體。

5. 若 credentials 不比對,則抛出 AuthenticationException 異常。  

這是對所有 Realm getAuthenticationInfo 實作的最進階别的工作流。在此方法中,Realm 可以自由地做任何它們想做 的,如記錄在審計日志的嘗試,更新資料記錄,或任何其他可以對該資料存儲的身份驗證嘗試有意義的東西。   唯一需要的東西就是,如果 credentials 比對給予的 principal(s),那麼傳回一個非空的 AuthenticationInfo 執行個體來代表 來自于該資料源的 Subject 帳戶資訊。 

c.Save Time               

直接實作 Realm 接口可能導緻時間消耗及錯誤。大多數人們選擇 AuthorizingRealm 抽象類的子類而不是從頭開始。 這個類實作了常用的 authentication 及 authorization 工作流來節省你的時間和精力。   

d.Credentials Matching

在上面的 realmauthentication 工作流中,Realm 不得不驗證 Subject 送出的 credentials(如,,密碼)必須比對存儲 在資料存儲中的 credentials。如果比對,則被認為身份驗證成功,同時系統還必須驗證終端使用者的身份。   Realm Credentials Matching                        這是每個 Realm 的責任,去比對送出的 credentials 和那些存儲在 Realm 後備資料存儲中的 credentials,而不是 Authenticator 的責任。每個 Realm 擁有有關私人資訊的 credentials 格式,存儲及能夠執行詳細的 credentials 比對, 然而 Authenticator 隻是一個普通的工作量元件。

credentials 的比對過程在所有應用程式中幾乎一樣,通常不一樣的是進行比較的資料。為了確定該過程是可插入及 可定制的如果需要的話,AuthenticatingRealm 及它的子類支援 CredentialsMatcher 來執行 credentials 對比的概念。   在發現帳戶資料後,它以及送出的 AuthenticationToken 用來代表一個 CredentialsMatcher 來判斷所送出的是否比對 所存儲的。   Shiro 擁有某些可以讓你立即使用的 CredentialsMatcher 實作,如 SimpleCredenticalsMatcher 和 HashedCredentialsMatcher,但如果你想為自定義的邏輯配置一個自定義的實作,你可以像下面一樣直接做:

shiro實戰系列(七)之Realm

 e.Simple Equality Check

所有 Shiro 立即可用的 Realm 的實作預設使用 SimpleCredentialsMatcher。SimpleCredentialsMatcher 執行一個普通的 直接平等檢查,關于存儲的帳戶 credentials 與在 AuthenticationToken 所送出的之間的檢查。 

例如,若一個 UsernamePasswordToken 被送出後,則 SimpleCredentialsMatcher 驗證該密碼實際上是否與存儲在數 據庫中的密碼相同。   SimpleCredentialsMatcher 不僅僅為字元串執行直接相等比較。它能夠處理大多數常用的位元組碼,像字元串,字元數 組,位元組數組,檔案及輸入流。請參考它的 JavaDoc 擷取更多。

f.Hashing Credentials

并非是存儲credentials 在其原始的 form 及執行原始/普通的比較,一個更安全的方式存儲終端使用者的credentials(如, 密碼)是在存儲它們到資料存儲之前将它們單向散列化。   這確定終端使用者的 credentials 絕不會以原始的 form 存儲,而且沒人會知道原始值。這是一個比純文字或原始比較 更為安全的機制,同時所有關注安全的應用程式應該較非哈希化的存儲更為喜歡。   為了支援這些首選的加密哈希政策,Shiro 提供了 HashedCredentialsMatcher 的實作配置在 realm 上而不是上述 SimpleCredentialsMatcher。   哈希 credentials 及 salting 和多個哈希疊代的好處超出了該 Realm 文檔的範圍,但絕對要閱讀 HashedCredentialsMatcher 的 JavaDoc,其中将詳細介紹這些細節。  

g.Hashing and Corresponding Matchers

那麼,你如何很容易地配置一個啟用 Shiro 的應用程式呢?   Shiro 提供了多個 HashedCredentialsMatcher 子類實作。你必須在你的 realm 中配置指定的實作來比對你 hash 化你用 戶 credentials 時使用的雜湊演算法。  

例如,假設你的應用程式為身份驗證使用使用者名/密碼對。由于上文所述的哈希憑據的好處,假設當你建立一個用 戶帳戶時,你想使用 SHA-256 算法單向散列使用者的密碼。你将哈希使用者輸入的純文字密碼并保持該值:

shiro實戰系列(七)之Realm

既然你使用 SHA-256 散列你使用者的密碼,你需要告訴 Shiro 使用合适的 HashedCredentialsMatcher 以比對你的哈希參 數選擇。在這個例子中,我們建立了一個随機的 salt 并執行 1024 次哈希疊代,為了強大的安全性(請參見 HashedCredentialsMatcher 的 JavaDoc 擷取原因)。這裡是完成這項工作的 Shiro INI 配置: 

shiro實戰系列(七)之Realm

h.SaltedAuthenticationInfo

為了確定這一工程,最後要做的事情是,你的 Realm 實作必須傳回一個 SaltedAuthenticationInfo 執行個體而不是一個普 通的 AuthenticationInfo 執行個體。SaltedAuthenticationInfo 接口確定在你建立使用者帳戶(如,user.setPasswordSalt(Salt); call above)時使用的 salt 能夠被 HashedCredentialsMatcher 引用.   HashedCredentialsMatcher 需要該 salt 為了能夠在送出的 AuthenticationToken 上執行相同的哈希技術來判斷該 token 是否比對你儲存在資料存儲中的東西。是以,如果你為使用者密碼使用 salting(而且你應該這樣做!!!),確定你 的 Realm 實作能夠通過傳回的 SaltedAuthenticationInfo 執行個體代表密碼。  

i.Disabling Authentication

如果出于某些原因,你不想用 Realm 對資料源執行身份驗證(也許是由于你隻想 Realm 執行授權),你可以徹底地 禁用 Realm 對身份驗證的支援通過從 Realm 的 support 方法傳回 false。然後你的 realm 在身份驗證嘗試中永遠不會 被通路到。   當然,至少需要一個能夠支援 AuthenticationTokens 且已配置的 Realm,如果你想驗證 Subjects