1、在web環境下使用Shiro使用
web
將 Shiro 集成到任何 Web 應用程序的最簡單的方法是在 web.xml 中配置 ContextListener 和 Filter,理解如何讀取 Shiro 的 INI 配置文件。算法
一、web.xml配置shell
<!-- Shiro --> <listener> <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class> </listener> <filter> <filter-name>ShiroFilter</filter-name> <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class> </filter> <filter-mapping> <filter-name>ShiroFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping>
這假設一個 Shiro INI Configuration 文件在如下兩個位置任意一個,並使用最早發現的那個:apache
/WEB-INF/shiro.ini數組
在classpath 根目錄下shiro.ini 文件安全
下面是上述配置所作的事情:session
EnvironmentLoaderListener 初始化一個Shiro WebEnvironment 實例(其中包含 Shiro 須要的一切操做,包括 SecurityManager ),使得它在 ServletContext 中可以被訪問。若是你須要在任什麼時候候得到WebEnvironment 實例,你能夠調用WebUtils.getRequiredWebEnvironment(ServletContext)。app
ShiroFilter 將使用此 WebEnvironment 對任何過濾的請求執行全部必要的安全操做。ide
最後,filter-mapping 的定義確保了全部的請求被 ShiroFilter 過濾,建議大多數 Web 應用程序使用以確保任何請求是安全的。ui
ShiroFilter filter-mapping
它一般可取的作法是在任何其餘 filter-mapping 聲明以前定義 ShiroFilter filter-mapping,以確保 Shiro 也能在那些過濾器 下工做的很好。
IniWebEnvironment 將會去讀取和加載 INI 配置文件。默認狀況下,這個類會自動地在下面兩個位置尋找 Shiro.ini 配置(按順序)。
/WEB-INF/shiro.ini
classpath:shiro.ini
它將使用最早發現的那個。 然而,若是你想把你的配置放在另外一位置,你能夠在 web.xml 中用contex-param 指定該位置。
<context-param>
<param-name>shiroConfigLocations</param-name>
<param-value>YOUR_RESOURCE_LOCATION_HERE</param-value>
</context-param>
默認狀況下,在 ServletContext.getResource 方法定義的規則下,param-value 是能夠被解析的。例如, /WEB-INF/some/path/shiro.ini。
3、INI Configuration配置
爲了確保具備共性的基於文本配置的途徑適用於任何環境並且減小對第三方的依賴,Shiro 支持使用 INI 建立 SecurityManager 對象視圖(graph)以及它支持的組件,INI 易讀易配置,很容易建立而且對大多數程序都很適合。
INI 基於文本配置,在獨立命名的區域內經過成對的鍵名/鍵值組成。鍵名在每一個區域內必須惟一,但在整個配置文件中並不須要這樣(這點和JDK的Properties不一樣),每個區域(section)能夠看做是一個獨立的Properties 定義。
這裏是一個 Shiro 能夠理解的各 section 的示例。
# ======================= # Shiro INI configuration # ======================= [main] # Objects and their properties are defined here, # Such as the securityManager, Realms and anything # else needed to build the SecurityManager [users] # The 'users' section is for simple deployments # when you only need a small number of statically-defined # set of User accounts. [roles] # The 'roles' section is for simple deployments # when you only need a small number of statically-defined # roles. [urls] # The 'urls' section is used for url-based security # in web applications. We'll discuss this section in the # Web documentation
[main]
[main]區域是配置程序 SecurityManager 實例及其支撐組件的地方,如 Realm。
經過INI配置像 SecurityManager 的對象實例及其支撐組件聽起來是一件很困難的事情,由於在這裏咱們只能用鍵名/鍵值對。但經過定義一些對象視圖(graphs)能夠理解的慣例,你發現你徹底能夠這樣作。Shiro 利用這些假定的慣例來實現一個簡單而簡明的配置途徑。
咱們常常將這種方法認爲是「可憐人的(poor man's)」的依賴注入,雖然不及成熟的Spring/Guice/JBoss的XML文件強大,但你會發現它能夠作不少事情並且並不複雜,固然當那配置途徑也可使用,但對 Shiro 來說並非必須的。
僅僅吊一下胃口,這裏是一個簡單的可使用的[main]配置,下面咱們會詳細介紹,但你可能發現你僅憑直覺就能夠理解一些。
[main]
sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher myRealm = com.company.security.shiro.DatabaseRealm myRealm.connectionTimeout = 30000 myRealm.username = jsmith myRealm.password = secret myRealm.credentialsMatcher = $sha256Matcher securityManager.sessionManager.globalSessionTimeout = 1800000
Defining an object 定義一個對象
在[main]中包含如下片斷。
[main]
myRealm = com.company.shiro.realm.MyRealm ...
這一行實例化了一個類型爲 com.company.shiro.realm.MyRealm 的對象實例而且使對象使用 myRealm 做爲名稱以便於未來引用和配置。
若是對象實例化時實現了 org.apache.shiro.util.Nameable 接口,Nameable.setName方法將被以該名(在此例中爲myRealm)命名的對象調用。
Setting object properties 設置對象屬性
Primitive Values 原始值
簡單的原始值屬性可使用下面的等於符號進行設置:
... myRealm.connectionTimeout = 30000 myRealm.username = jsmith ...
這些配置行轉換爲方法調用就是:
... myRealm.setConnectionTimeout(30000); myRealm.setUsername("jsmith"); ...
怎麼作到的呢?它假定全部對象都是兼容 JavaBean 的 POJO。在設置這些屬性時,Shiro 默認使用 Apache 通用的BeanUtils 來完成這項複雜的工做,因此雖然 INI 值是文本,BeanUtils 知道如何將這些字符串值轉換爲適合的原始值類型並調用合適的 JavaBeans 的 setter 方法。
Reference Values 引用值
若是你想設置的值並非一個原始值,而是另外一個對象怎麼辦呢?你可使用一個 $ 符來引用一個以前定義的實例,如:
... sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher ... myRealm.credentialsMatcher = $sha256Matcher ...
這定義了名爲 sha256Matcher 的對象而且使用 BeanUtils 將其設置到myRealm 的實例中(經過調用 myRealm.setCredentialsMatcher(sha256Matcher) 方法)。
Nested Properties 嵌套屬性
經過在等號左側使用點符號,你能夠獲得你但願設置對象視圖最終的對象/屬性,例以下面這行配置:
... securityManager.sessionManager.globalSessionTimeout = 1800000 ...
轉換邏輯爲(經過BeanUtils):
securityManager.getSessionManager().setGlobalSessionTimeout(1800000);
用這種方法訪問的層數須要多深能夠有多深: object.property1.property2....propertyN.value = blah
BeanUtils 屬性支持
BeanUtils 支持任何指定的屬性操做,在 Shiro [main] 區域中setProperty方法將被調用,包括集合(set)/列表(list)/圖(map),查看Apache Commons BeanUtils Website和文檔瞭解更多的信息。
Byte Array Values 字節數組值
由於原始的字節數組不能直接在文本中定義,咱們必須使用字節數組的文本編碼。可使用64位編碼(默認)或者16位編碼,默認爲64位編碼由於使用64位編碼實際文字會少一些--它擁有很大的編碼表,這意味着你的標識會更短(對於文本配置來說會好一些)。
# The 'cipherKey' attribute is a byte array. By default, text values # for all byte array properties are expected to be Base64 encoded: securityManager.rememberMeManager.cipherKey = kPH+bIxk5D2deZiIxcaaaA== ...
若是你想使用16位編碼,你必須在字串前面加上 0x 前綴:
securityManager.rememberMeManager.cipherKey = 0x3707344A4093822299F31D008
Collection Properties 集合屬性
列表(Lists)、集合(Sets)、圖(Maps)能夠像其它屬性同樣設置--直接設置或者像嵌套屬性同樣,對於列表和集合,只需指定一個逗號分割的值集或者對象引用集。
如定義一些SessionListeners:
sessionListener1 = com.company.my.SessionListenerImplementation ... sessionListener2 = com.company.my.other.SessionListenerImplementation ... securityManager.sessionManager.sessionListeners = $sessionListener1, $sessionListener2
對於圖(Maps),你能夠指定以逗號分割的鍵-值對列表,每一個鍵-值之間用冒號分割
object1 = com.company.some.Class object2 = com.company.another.Class ... anObject = some.class.with.a.Map.property anObject.mapProperty = key1:$object1, key2:$object2
在上面的例子中,$object1 引用的對象將存於鍵 key1 之下,也就是map.get("key1") 將返回 object1。你也可使用其它對象做爲鍵值:
anObject.map = $objectKey1:$objectValue1, $objectKey2:$objectValue2 ...
Considerations 注意事項
Order Matters 順序問題
上述 INI 格式和約定很是方便也很是易懂,但它並無另一種 text/XML的配置路徑強大,經過上述途徑進行配置須要知道很是重要的一件事情就是順序問題!
當心
每個對象實例以及每個指定的值都將按照其在 [main] 區域中產生的順序的執行,這些行最終轉換爲 JavaBeans 的 getter/setter 方法調用,這些方法按一樣的順序調用。
當你寫配置文件的時候要牢記於此。
Overriding Instances 覆蓋實例
每個對象均可以被後定義的新實例覆蓋,例如,第二個myRealm定義將重寫第一個:
... myRealm = com.company.security.MyRealm ... myRealm = com.company.security.DatabaseRealm ...
這樣的結果是 myRealm 是 com.company.security.DatabaseRealm 實例而前面的實例不會被使用(會做爲垃圾回收)。
Default SecurityManager 默認Default SecurityManager
你可能注意到在以上全部例子中都沒有定義 SecurityManager,而咱們直接設置其嵌套屬性
myRealm = ... securityManager.sessionManager.globalSessionTimeout = 1800000 ...
這是由於securityManager實例是特殊的--它已經爲你實例化過了而且準備好了,因此你並不須要知道指定的實例化SecurityManager的實現類。
固然,若是你確實想指定你本身的實現類,你能夠像上面的覆蓋實例那樣定義你本身的實現:
... securityManager = com.company.security.shiro.MyCustomSecurityManager ...
固然,不多須要這樣--Shiro 的 SecurityManager 實現能夠按需求進行定製,你可能要問一下本身(或者用戶羣)你是否真的須要這樣作。
[users]
[users]區域容許你定義一組靜態的用戶賬號,這對於那些只有少數用戶賬號而且用戶賬號不須要在運行時動態建立的環境來講很是有用。下面是一個例子:
[users]
admin = secret lonestarr = vespa, goodguy, schwartz darkhelmet = ludicrousspeed, badguy, schwartz
自動生成IniRealm
定義非空的[users]或[roles]區域將自動建立org.apache.shiro.realm.text.IniRealm 實例,在[main]區域下生成一個可用的 iniRealm ,你能夠像上面配置其它對象那樣配置它。
Line Format 格式
[users]區域下每一行必須和下面的形式一致:
username = password, roleName1, roleName2, ..., roleNameN
等號左邊的值是用戶名;
等號右側第一個值是用戶密碼,密碼是必須的;
密碼以後用逗號分割的值是賦予用戶的角色名,角色名是可選的。
Encrypting Passwords 密碼加密
若是你不但願[users]區域下的密碼以明文顯示,你能夠用你喜歡的哈希算法(MD5, Sha1, Sha256, 等)來加密它們,將加密後的字符串做爲密碼值,默認的,密碼建議用16位編碼算法,但也能夠用64位編碼算法替代(以下)
簡單的安全密碼
爲了節約時間得到最佳實踐,你可使用 Shiro 的 Command Line Hasher,它能夠加密密碼和其它類型的資源,尤爲使給 INI[user] 密碼加密變得很是簡單。
一旦你指定了加密後的密碼值,你必須告訴 shiro 它們是加密的,你能夠經過配置配置在[main]隱含建立的iniRealm相應的CredentialsMatcher 實現來告知你使用的哈希算法:
[main]
... sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher ... iniRealm.credentialsMatcher = $sha256Matcher ...
[users]
# user1 = sha256-hashed-hex-encoded password, role1, role2, ... user1 = 2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b, role1, role2, ...
你能夠像配置其餘對象那樣配置 CredentialsMatcher 的全部屬性,例如,指定使用salting或者有多少hash iterations執行,能夠查看org.apache.shiro.authc.credential.HashedCredentialsMatcher Java文檔更好地理解 hashing 策略,可能會頗有幫助。
例如,若是你用64位編碼方式取代了16位編碼方式,你應該指定:
[main]
... # true = hex, false = base64: sha256Matcher.storedCredentialsHexEncoded = false
[roles]
[roles]區域容許你將權限和在[users]定義的角色對應起來,一樣的,這對於那些只有少數用戶賬號而且用戶賬號不須要在運行時動態建立的環境來講很是有用。下面是一個例子:
[roles]
# 'admin' role has all permissions, indicated by the wildcard '*' admin = * # The 'schwartz' role can do anything (*) with any lightsaber: schwartz = lightsaber:* # The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with # license plate 'eagle5' (instance specific id) goodguy = winnebago:drive:eagle5
Line Format 格式
[roles]區域下的每一行必須用下面的格式定義角色-權限的鍵/值對應關係。
rolename = permissionDefinition1, permissionDefinition2, ..., permissionDefinitionN
權限定義能夠是很是隨意的字符串,但大部分用戶仍是但願使用易用而靈活的和org.apache.shiro.authz.permission.WildcardPermission形式一致的字符串格式。查看 Permissions 文檔獲取更多關於權限的信息和你能夠如何利用它爲你服務。
內部用法
注意若是一個特定的權限定義須要用到逗號分隔(如:printer:5thFloor:print,info),你須要將該定義用雙引號括起來從而避免出錯:"printer:5thFloor:print,info"。
沒有權限的角色
若是你有不須要權限的角色,不須要將它們列入[roles]區域,僅僅在 [users]區域定義角色名就能夠建立它們(若是它們尚不存在)。
[urls]
在urls 項的每一行格式以下:
URL_Ant_Path_Expression = Path_Specific_Filter_Chain