Apache Shiro 安全框架-配置詳解
Apache Shiro安全框架配置詳解
---By Solo Cui
Shiro可以在任何環境下工作,從簡單的命令行應用程序到最大的企業集群應用程序。 由於這種環境的多樣性,有許多適合配置的配置機制。 本節僅介紹Shiro內核支持的配置機制。
另外,Shiro的SecurityManager實現和所有支持組件都與JavaBean兼容。 這使得Shiro可幾乎任何配置格式進行配置,例如常規Java、XML(Spring,JBoss,Guice等)、YAML、JSON、Groovy Builder標記等等。
下面主要介紹如下兩種配置模式,即編程式配置和INI文件式配置。
6.1、編程式配置
令人驚訝的是,僅需3行代碼,您就可以擁有適用於多種應用的功能齊全的Shiro環境。 那有多容易a!?
6.1.1、SecurityManager對象圖
正如架構章節中所討論的,Shiro的SecurityManager實現本質上是特定安全組件嵌套的模塊化對象圖。 因為它們也是JavaBeans兼容的,所以可以調用任何嵌套的組件getter和setter方法來配置SecurityManager及其內部對象圖。
例如,如果您想將SecurityManager實例配置為使用自定義的SessionDAO來定製會話管理,則可以使用嵌套的會話管理器SessionManager的setSessionDAO方法直接設置SessionDAO:
直接使用方法調用,您可以配置SecurityManager的對象圖的任何部分。
像程序化定製一樣簡單,但是,它並不代表大多數真實世界應用程序的理想配置。 編程配置可能不適合您的應用程序的原因有幾個:
? 它需要你知道並實例化一個直接的實現。 如果你不必知道具體的實現以及在哪裡找到它們,那將會更好。
? 由於Java的類型安全性,您需要將通過get*方法獲得的對象轉換到其特定實現。 如此多的轉換是醜陋的、冗長的,並且緊耦合到實現類。
?SecurityUtils.setSecurityManager方法調用使實例化的SecurityManager實例成為一個VM靜態單例,對於許多應用程序來說,如果在同一個JVM上運行多個啟用Shiro的應用程序,這可能會導致問題。 如果實例是單例應用程序,但不是靜態內存引用,那可能會更好。
?每次想要更改Shiro配置時,都需要您重新編譯應用程序。
但是,即使存在這些警告,直接編程操作方式在內存受限的環境中仍然很有價值,例如智能手機應用程序。 如果您的應用程序不在內存受限的環境中運行,您會發現基於文本的配置更易於使用和閱讀。下文就詳細闡述基於INI文本文件的配置方式。
6.2、INI文件式配置
大多數應用程序都受益於基於文本的配置,這些配置可以獨立於源代碼進行修改,甚至可以讓那些不熟悉Shiro API的人員更容易理解。
為確保基於文本的通用配置機制能夠在所有具有最小第三方依賴性的環境中工作,Shiro支持INI格式來構建SecurityManager對象圖及其支持組件。 INI文件易於閱讀、易於配置、設置簡單,並且適合大多數應用。
6.2.1 從INI創建SecurityManager
以下是如何基於INI配置來構建安全管理器SecurityManager的兩個示例。
1-從INI資源文件創建SecurityManager
我們可以從INI資源路徑創建SecurityManager實例。 當分別以file:,classpath 或url:為前綴時,可以從文件系統,類路徑或URL獲取資源。 本示例使用Factory從類路徑的根目錄獲取shiro.ini文件並返回SecurityManager實例:
2-從INI實例構建SecurityManager
現在我們已經知道如何從INI配置構造一個SecurityManager,讓我們來看看如何定義一個Shiro INI配置。
6.2.2 INI文件組成部分
INI基本上是由唯一命名的部分組成的鍵/值對組成的文本配置。 鍵在每個部分都是唯一的,而不是整個配置(與JDK屬性不同)。 可是每個部分可以像單個屬性定義一樣被查看。
注釋行可以井號(# - 又名"hash","pound"或"number"符號)或分號(";")開始
這是Shiro所能理解的一個例子的部分:
1-[main]部分
[main]部分是配置應用程序的SecurityManager實例及其任何依賴關係(如Realm)的位置。
與SecurityManager或其任何依賴關係類似,配置對象實例聽起來像是一個難以處理的INI文件,文件中我們只能使用名稱/值對。 但是通過對象圖的一些約定和理解,你會發現你可以做很多事情。 Shiro使用這些假設來實現簡單又相當凝練的配置機制。
我們經常喜歡將這種方法稱為"窮人的"依賴注入,儘管沒有像完整的Spring / Guice / JBoss XML文件那樣強大,但是您會發現它完成得相當多,沒有太多複雜性。 當然,其他的配置機制也可以使用,但不需要使用Shiro。
只是為了激起你的胃口,這是一個有效的[main]配置的例子。 我們將在下面詳細介紹它,但是你可能會發現,憑直覺你已經很清楚已經發生了什麼:
定義對象
考慮以下[main]部分片段:
設置對象屬性
1)基本值
簡單的基本屬性可以通過使用等號來賦值:
這些配置行轉換為方法調用如下所示:
這怎麼可能? 因為它假定所有對象都是Java Bean兼容的POJO。
在背後,Shiro默認使用Apache Commons BeanUtils在設置這些屬性時完成所有繁重的工作。 因此,雖然INI值是文本,但BeanUtils知道如何將字元串值轉換為適當的基本類型,然後調用相應的JavaBeans setter方法。
2)引用值
如果你需要設置的值不是基本的,而是另一個對象呢? 那麼,您可以使用美元符號($)來引用先前定義的實例。 例如:
這只是簡單地定位名稱sha256Matcher定義的對象,然後使用BeanUtils在myRealm實例上設置該對象(通過調用myRealm.setCredentialsMatcher(sha256Matcher)方法)。
3)嵌套屬性
在INI行等號的左側使用點符號,可以遍歷一個對象圖以獲得您想要設置的最終對象/屬性。 例如,下面這個配置行:
將(通過BeanUtils)轉換為以下邏輯:
4)位元組數組值
由於原始位元組數組不能以文本格式本地化指定,因此我們必須使用位元組數組的文本編碼。 這些值可以指定為Base64編碼的字元串(默認值),也可以指定為十六進位編碼的字元串。 默認是Base64,因為Base64編碼需要較少的實際文本來表示值 - 它有一個更大的編碼字母表,這意味著您的令牌更短(這對於文本配置更好一點)。
但是,如果您更願意使用十六進位編碼,則必須在字元串標記前綴0x("零""x"):
5)集合屬性
列表、集合和映射可以像任何其他屬性一樣設置——直接或作為嵌套屬性。 對於集合和列表,只需指定逗號分隔的一組值或對象引用即可。如下面的示例:
對於映射,您可以指定以逗號分隔的鍵值對列表,其中每個鍵值對由冒號":"分隔。
在上例中,由$object1引用的對象將位於String鍵key1下的映射中,即map.get("key1")返回object1。 您也可以使用其他對象作為鍵,如下:
6)注意事項
6-1)順序問題
上述INI格式和約定非常方便且易於理解,但並不像其他文本/基於XML的配置機制那麼強大。 在使用上述機制時要理解的最重要的事情是順序問題(Order Matters)!
小心:每個對象實例化和每個值賦值是按它們在[main]節點中出現的順序執行。這些行最終轉化為JavaBeans的getter/setter方法調用,所以這些方法以相同的順序調用!
在編寫配置時請記住這一點。
7)覆蓋實例
任何對象都可以被稍後在配置中定義的新實例覆蓋。 例如,第二個myRealm定義會覆蓋第一個:
8)默認安全管理器(SecurityManager)
您可能在上面的完整示例中已經注意到SecurityManager實例的類未定義,並且我們直接跳轉到設置嵌套屬性:
這是因為securityManager實例是一個特殊的實例 - 它已經為您實例化並準備就緒,因此您不需要知道要實例化的特定SecurityManager實現類。
當然,如果你真的想指定你自己的實現,你可以按照上面的"重寫實例"一節中的規定來定義你的實現:
當然,這很少需要——Shiro的SecurityManager實現是非常可定製的,通常可以配置任何必要的東西。 但您可能需要問自己(或用戶列表)是否您確實需要這樣做?
2-[users]部分
[users]部分允許您定義一組靜態的用戶帳戶。 這在用戶帳戶數量非常少的環境或用戶帳戶不需要在運行時動態創建的環境中非常有用。 這是一個例子:
提示:自動iniRealm
1)行格式
[users]部分中的每一行必須符合以下格式:
=,roleName1,roleName2, …,roleNameN
?等號左邊的值是用戶名
?等號右邊的第一個值是用戶的密碼。 密碼是必需的。
?密碼後的任何逗號分隔值是分配給該用戶的角色的名稱。 角色名稱是可選的。
2)加密密碼
如果您不希望[users]部分的密碼為純文本格式,則可以使用您喜歡的哈希演算法(MD5,Sha1,Sha256等)對其進行加密,無論您是否喜歡,並將結果字元串用作密碼值。 默認情況下,密碼字元串預計為十六進位編碼,但可以配置為Base64編碼(參見下文)。
提示:便捷的密碼安全處理
為了節省時間並使用最佳實踐,您可能需要使用Shiro的命令行哈希器,它會散列密碼以及任何其他類型的資源。加密INI[users]部分密碼特別方便。
一旦你指定了散列文本密碼值,你必須告訴Shiro它們已被加密。 您可以通過在main部分配置隱式創建的iniRealm來使用與您指定的哈希演算法相對應的適當的CredentialsMatcher實現:
您可以像任何其他對象一樣配置CredentialsMatcher上的任何屬性以反映您的散列策略,例如,指定是否使用salting或執行多少個散列迭代。 請參閱org.apache.shiro.authc.credential.HashedCredentialsMatcher JavaDoc(http://shiro.apache.org/static/current/apidocs/org/apache/shiro/authc/credential/HashedCredentialsMatcher.html)以更好地理解哈希策略,以及它們是否對您有用。
例如,如果用戶的密碼字元串是Base64編碼,而不是默認的十六進位,則可以指定:
3-[roles]部分
[roles]部分允許您將許可權與用戶部分中定義的角色相關聯。 同樣,這在具有少量角色的環境中或角色不需要在運行時動態創建的情況下很有用。 這是一個例子:
1)行格式
[roles]部分中,每一行必須必須以下列格式定義角色許可權(s)鍵/值映射:
=permissionDefinition1,permissionDefinition2, …,permissionDefinitionN
提示:內部逗號
請注意,如果單個permissionDefinition需要在內部逗號分隔(例如,printer:5thFloor:print,info),則需要用雙引號(")括住該定義以避免解析錯誤:"printer:5thFloor:print,info "。
如果您的角色不需要許可權關聯,則不需要在[roles]部分列出它們。 如果它還不存在,只要在[users]部分中定義角色名稱就足以創建角色。
4-[urls]部分
本章節及其選項在Web章節中有描述。這裡重複描述,具體內容如下:
[urls]部分中每行的格式如下:
_URL_Ant_Path_Expression_= _Path_Specific_Filter_Chain_
示例如下:
接下來我們將闡述這些行的意思。
等號(=)左側的標記是相對於Web應用程序的上下文根的Ant式路徑表達式。
例如,假設您有以下[urls]行:
/account/** = ssl, authc
此行描述的是:"對我的應用程序/account路徑或其任何子路徑(/account/foo,/account/bar/baz等)的任何請求都會觸發"ssl,authc"過濾器鏈"。 我們下面將描述過濾器鏈。
請注意,所有路徑表達式都與您的應用程序的根上下文相關。 這意味著如果您有一天將應用程序部署到www.somehost.com/myapp,然後又將其部署到www.anotherhost.com(沒有"myapp"子路徑),則模式匹配仍然可以工作。 所有路徑都與HttpServletRequest.getContextPath()值有關。
小心:順序問題
URL路徑表達式按照它們傳入請求的定義順序和「FIRST MATCH WINS」(首先匹配獲勝)進行鑒定。例如,讓我們假設有以下定義鏈:
如果傳入的請求打算到達/account/signup/index.html(可供所有"匿名"用戶訪問),它將永遠不會被處理!原因是/ account / **模式首先匹配傳入請求,並將所有剩餘定義"短路"。
永遠記得根據FIRST MATCH WINS策略定義您的過濾器鏈!
1)過濾鏈定義
等號(=)右側的標記是用逗號分隔的過濾器列表,用於執行與該路徑匹配的請求。 它必須符合以下格式:
filter1[optional_config1], filter2[optional_config2], ..., filterN[optional_configN]
其中:
? filterN是[main]部分中定義的過濾器bean的名稱,並且
? [optional_configN] 是一個可選的括弧內的字元串,對那個特定路徑的特定過濾器有意義(每個過濾器,特定於路徑的配置!)。 如果過濾器不需要該URL路徑的特定配置,則可以放棄括弧,因此filterN[] 只會變成filterN。
並且因為過濾令牌定義了鏈(又名列表),請記住該順序很重要! 按照您希望請求流經的順序定義您的逗號分隔鏈列表。
最後,如果不滿足其必要條件(例如,執行重定向,用HTTP錯誤代碼響應,直接渲染等),每個過濾器都可以自由處理響應。 否則,按照預期,它會允許請求經由過濾鏈,到最終的目標視圖。
提示:
能夠對特定路徑的配置作出反應,即過濾器令牌的[optional_configN]部分,是Shiro過濾器可用的獨特功能。
2)可用過濾器
可用於過濾器鏈定義的"過濾器"池在[main]部分定義。 在main部分分配給他們的名稱是過濾器鏈定義中使用的名稱。 例如:
3)默認過濾器
運行Web應用程序時,Shiro將創建一些有用的默認過濾器實例,並自動在main部分使其可用。 您可以像在任何其他bean中一樣配置它們,並在鏈定義中引用它們。 例如:
可用的默認Filter實例自動由枚舉類DefaultFilter完成定義,枚舉的name欄位是可用於配置的名稱。 他們是:
4)啟用禁止過濾器
與任何過濾器鏈定義機制(web.xml,Shiro的INI等)一樣,只需將過濾器包含在過濾器鏈定義中即可啟用該過濾器,並通過將其從鏈定義中刪除來禁用過濾器。
但Shiro 1.2中新增的一項新功能是可以啟用或禁用濾波器,而無需從濾波器鏈中刪除濾波器。 如果啟用(默認設置),則會按預期過濾請求。 如果禁用,則過濾器將允許請求立即通過FilterChain中的下一個元素。 通常可以基於配置屬性觸發過濾器的啟用狀態,或者甚至可以基於每個請求觸發它。
這是一個強大的概念,因為根據某些要求啟用或禁用過濾器通常比更改靜態過濾器鏈定義更方便,而靜態過濾器鏈定義是永久性且不靈活的。
Shiro通過它的OncePerRequestFilter抽象父類完成此操作。 所有Shiro的開箱即用濾波器實現都是這個子類的一個子類,因此可以在不從濾波器鏈中刪除它們的情況下啟用或禁用它們。 如果您也需要此功能,您可以將此類繼承為您自己的過濾器實現*。
*SHIRO-224(https://issues.apache.org/jira/browse/SHIRO-224)將有希望為任何過濾器啟用此功能,而不僅僅是那些子類OncePerRequestFilter。 如果這對你很重要,請為此問題投票。
5)通用啟用/禁用
OncePerRequestFilter(及其所有子類)支持跨所有請求啟用/禁用過濾器,與基於每個請求的一樣。
通常為所有請求啟用或禁用過濾器是通過將其enabled屬性設置為true或false來完成的。 默認設置為true,因為大多數濾波器在鏈中配置時是固化需要執行的。
例如,在shiro.ini中:
這個例子表明,很多URL路徑可能都需要一個請求必須通過SSL連接來保護。 在開發過程中設置SSL可能令人沮喪且費時。 在開發過程中,您可以禁用SSL過濾器。 部署到生產環境時,您可以使用一個配置屬性啟用它 - 這比手動更改所有URL路徑或維護兩個Shiro配置要容易得多。
6)特殊請求的啟用和禁用
OncePerRequestFilter根據isEnabled(request,response)方法實際確定是啟用還是禁用過濾器。
此方法默認返回enabled屬性的值,該屬性用於通常啟用/禁用上述所有請求。 如果您想根據請求特定條件啟用或禁用過濾器,則可以覆蓋OncePerRequestFilter.isEnabled(request,response)方法以執行更具體的檢查。
7)特殊路徑的啟用和禁用
Shiro的PathMatchingFilter(OncePerRequestFilter的一個子類)可以根據被過濾的特定路徑對配置做出反應,這意味著除了傳入的請求和響應之外,還可以根據路徑和路徑特定的配置啟用或禁用過濾器。
如果您需要能夠響應匹配路徑和特定於路徑的配置以確定過濾器是啟用還是禁用,而不是重寫OncePerRequestFilter.isEnabled(request,response)方法,則可以覆蓋PathMatchingFilter.isEnabled(request,response ,path,pathConfig)方法。
※想成為一名Web開發者?或許應該學習Node.js而不是PHP
TAG:3T趣課堂 |