# 4. Configuration 配置
# 4. Configuration 配置
Shiro 可以在任何環境下工作,從簡單的命令行程序到大型企業級集群項目,因為環境的多樣化,可以通過許多途徑來配合當前環境的配置方式進行配置,在本章我們來了解一下 Shiro 核心支持的配置方式。
*多種配置選擇*
*Shiro 的 SecurityManager 的實現和其所依賴的組件都是 JavaBean,所以可以用多種形式對 Shiro 進行配置,比如XML(Spring, JBoss, Guice, 等等),[YAML](http://www.yaml.org/), JSON, Groovy Builder markup,及其它,INI 只是 Shiro 一種最基本的配置方式,使得其可以在任何環境中進行配置比如在那些沒有以上配置形式的環境中。*
## Programmatic Configuration 在程序中配置
創建一個 SecurityManager 并使之可用最簡單的方法就是創建一個org.apache.shiro.mgt.DefaultSecurityManager 對象并且將它寫入代碼,例如:
```
Realm realm = //實例化或獲得一個Realm的實例。我們將稍后討論Realm。
SecurityManager securityManager = new DefaultSecurityManager(realm);
//使SecurityManager實例通過靜態存儲器對整個應用程序可見:
SecurityUtils.setSecurityManager(securityManager);
```
僅僅三行代碼,你就可以擁有一個適用于任何程序的功能全面的 Shiro 環境,多么簡單。
### SecurityManager Object Graph
如同我們在架構([Architecture](3.%20Architecture%20%E6%9E%B6%E6%9E%84.md) )中討論過的,Shiro SecurityMangger 本質上是一個由一套安全組件組成的對象模塊視圖(graph),因為與 JavaBean兼容,所以可以對所有這些組件調用的 getter 和 setter 方法來配置SecurityManager 和它的內部對象視圖。
例如,你想用一個自定義的 SessionDAO 來定制 [Session Management](http://shiro.apache.org/session-management.html)從而配置一個 SecurityManager 實例,你就可以使用 SessionManager 的 setSessionDAO 方法直接 set 這個 SessionDAO。
```
...
DefaultSecurityManager securityManager = new DefaultSecurityManager(realm);
SessionDAO sessionDAO = new CustomSessionDAO();
((DefaultSessionManager)securityManager.getSessionManager()).setSessionDAO(sessionDAO);
...
```
使用這些函數,你可以配置 SecurityManager 視圖(graph)中的任何一部分。
雖然在程序中配置很簡單,但它并不是我們現實中配置的完美解決方案。在幾種情況下這種方法可能并不適合你的程序:
- 它需要你確切知道并實例化一個直接實現(direct implementation),然而更好的做法是你并不需要知道這些實現也不需要知道從哪里找到它們。
- 因為JAVA類型安全的特性,你必須對通過 get\* 獲取的對象進行強制類型轉換,這么多強制轉換非常的丑陋、累贅并且會和你的類緊耦合。
- SecurityUtils.setSecurityManager 方法會將 SecurityManager 實例化為虛擬機的單獨靜態實例,在大多數程序中沒有問題,但如果有多個使用 Shiro 的程序在同一個 JVM 中運行時,各程序有自己獨立的實例會更好些,而不是共同引用一塊靜態內存。
- 改變配置就需要重新編譯你的程序。
然而,盡管有這些不足,在程序中定制的這種方法在限制內存(memory-constrained )的環境中還是很有價值的,像智能電話程序。如果你的程序不是運行在一個限制內存的環境中,你會發現基于文本的配置會更易讀易用。
## INI Configuration 配置
大多數程序已經改為使用基于文本的配置,不需要依靠代碼就可進行修改,對于不熟悉Shiro API的人來說,也易于理解。
為了確保具有共性的基于文本配置的途徑適用于任何環境而且減少對第三方的依賴,Shiro 支持使用 INI 創建 SecurityManager 對象視圖(graph)以及它支持的組件,INI 易讀易配置,很容易創建并且對大多數程序都很適合。
### Creating a SecurityManager from INI 通過INI資源創建 SecurityManager
這里舉兩個通過INI配置創建SecurityManager的例子。
#### SecurityManager from an INI resource 從INI資源創建SecurityManager
我們可以從一個INI資源路徑創建一個 SecurityManager 實例,資源可以通過文件系統(前綴為file:)、類路徑(classpath:)或者URL(url:)獲得,下面的例子使用一個 Factory 從類路徑根目錄加載 shiro.ini 并返回一個 SecurityManager 實例。
```
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.util.Factory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.config.IniSecurityManagerFactory;
...
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
```
#### SecurityManager from an INI instance 通過INI實例創建SecurityManager
INI 配置可以通過[org.apache.shiro.config.Ini](http://shiro.apache.org/static/current/apidocs/org/apache/shiro/config/Ini.html) 類用程序方式創建,這個 INI 類類似于 JDK 的[java.util.Properties](http://download.oracle.com/javase/6/docs/api/java/util/Properties.html)類,但支持通過section 名分割。例子如下:
```
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.util.Factory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.config.Ini;
import org.apache.shiro.config.IniSecurityManagerFactory;
...
Ini ini = new Ini();
//populate the Ini instance as necessary
...
Factory<SecurityManager> factory = new IniSecurityManagerFactory(ini);
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
```
現在我們知道如何使用 INI 配置文件創建一個 SecurityManager,讓我們仔細了解一下如何定義一個 shiro INI配置文件。
### INI Sections
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](http://en.wikipedia.org/wiki/JavaBean) 的 [POJO](http://en.wikipedia.org/wiki/Plain_Old_Java_Object)。在設置這些屬性時,Shiro 默認使用 Apache 通用的 [BeanUtils](http://commons.apache.org/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](http://commons.apache.org/beanutils/v1.8.2/apidocs/org/apache/commons/beanutils/BeanUtils.html#setProperty%28java.lang.Object,%20java.lang.String,%20java.lang.Object%29)方法將被調用,包括集合(set)/列表(list)/圖(map),查看[Apache Commons BeanUtils Website](http://commons.apache.org/beanutils/)和文檔了解更多的信息。*
##### 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](http://shiro.apache.org/command-line-hasher.html),它可以加密密碼和其它類型的資源,尤其使給 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](http://shiro.apache.org/static/current/apidocs/org/apache/shiro/authc/credential/HashedCredentialsMatcher.html) 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](http://shiro.apache.org/static/current/apidocs/org/apache/shiro/authz/permission/WildcardPermission.html)形式一致的字符串格式。查看 [Permissions](../II.%20Core%20%E6%A0%B8%E5%BF%83/6.1.%20Permissions%20%E6%9D%83%E9%99%90.md) 文檔獲取更多關于權限的信息和你可以如何利用它為你服務。
*內部用法*
*注意如果一個特定的權限定義需要用到逗號分隔(如:printer:5thFloor:print,info),你需要將該定義用雙引號括起來從而避免出錯:"printer:5thFloor:print,info"。*
*沒有權限的角色*
*如果你有不需要權限的角色,不需要將它們列入\[roles\]區域,僅僅在 \[users\]區域定義角色名就可以創建它們(如果它們尚不存在)。*
#### \[urls\]
該區域選項將在[Web](../III.%20Web%20Applications/10.%20Web.md)章節討論。
## 為文檔加把手
我們希望這篇文檔可以幫助你使用 Apache Shiro 進行工作,社區一直在不斷地完善和擴展文檔,如果你希望幫助 Shiro 項目,請在你認為需要的地方考慮更正、擴展或添加文檔,你提供的任何點滴幫助都將擴充社區并且提升 Shiro。
提供你的文檔的最簡單的途徑是將它發送到用戶[論壇](http://shiro-user.582556.n2.nabble.com/)或[郵件列表](http://shiro.apache.org/mailing-lists.html)
*譯者注:*如果對本中文翻譯有疑議的或發現勘誤歡迎指正,[點此](https://github.com/waylau/apache-shiro-1.2.x-reference/issues)提問。
- Introduction
- 1. Introduction 介紹
- 2. Tutorial 教程
- 3. Architecture 架構
- 4. Configuration 配置
- 5. Authentication 認證
- 6. Authorization 授權
- 6.1. Permissions 權限
- 7. Realms
- 8. Session Management
- 9. Cryptography 密碼
- 10. Web
- 10.1. Configuration 配置
- 10.2. 基于路徑的 url 安全
- 10.3. Default Filters 默認過濾器
- 10.4. Session Management
- 10.5. JSP Tag Library
- 11. Caching 緩存
- 12. Concurrency & Multithreading 并發與多線程
- 13. Testing 測試
- 14. Custom Subjects 自定義 Subject
- 15. Spring Framework
- 16. Guice
- 17. CAS
- 18. Command Line Hasher
- 19. Terminology 術語
- 20. 10 Minute Tutorial 十分鐘教程
- 21. Beginner's Webapp Tutorial 初學者web應用教程
- 22. Application Security With Apache Shiro 用Shiro保護你的應用安全
- 23. CacheManager 緩存管理
- 24. Apache Shiro Cryptography Features 加密功能