# 6.1. Permissions 權限
# 6.1. Permissions 權限
Shiro定義了一個許可聲明,定義了一個明確的行為或行動。 這是一個原始功能的聲明在一個應用程序而已。 權限是最低級別構造安全策略,他們只明確定義應用程序可以做“什么”。
他們不描述"誰"能夠執行的操作。
一些權限的例子:
- 打開一個文件
- 瀏覽'/user/list' 網頁
- 打印文件
- 刪除用戶‘jsmith’
規定“誰”(用戶)允許做“什么”(權限)在某種程度上是分配用權限的一種習慣做法。這始終是通過應用程序數據模型來完成的,并且在不同應用程序之間差異很大。
例如,權限可以組合到一個角色中,且該角色能夠關聯一個或多個用戶對象。或者某些應用程序能夠擁有一組用戶,且這個組可以被分配一個角色,通過傳遞的關聯,意味著所有在該組的用戶隱式地獲得了該角色的權限。
如何授予用戶權限可以有很多變化——應用程序基于應用需求來決定如何使其模型化。
## Wildcard Permissions 通配符的權限
上述權限的例子,“打開文件”、“瀏覽'/user/list' 網頁”,等都是有效的權限。 然而,計算來解釋這些自然語言字符串和確定用戶是否允許執行這一行為這將是非常困難的。
為了使更容易處理但仍可讀權限語句,Shiro 提供強大的和直觀的語法我們稱之為 WildcardPermission 。
### Simple Usage 簡單示例
你想保護訪問貴公司的打印機,這樣有些人可以打印到特定的打印機,而其他人可以查詢什么工作目前在隊列中。
一個非常簡單的方法是使用授予用戶“queryPrinter”權限。 然后你可以通過調用檢查用戶是否有 queryPrinter 權限:
```
subject.isPermitted("queryPrinter")
```
這是(大部分)相當于
```
subject.isPermitted( new WildcardPermission("queryPrinter") )
```
但遠不只這些。
簡單的權限字符串可能在簡單的應用程序中工作的很好,但它需要你擁有像"printPrinter","queryPrinter","managePrinter"等權限。你還可以通過使用通配符授予用戶"\*"權限(賦予此權限構造它的名字),這意味著他們在整個應用程序中擁有了所有的權限。
但使用這種方法沒有辦法說明用戶具有“所有打印機權限”。 出于這個原因,,Wildcard Permissions(通配符權限)支持多層次的權限管理。
### Multiple Parts 多個部分
通配符權限支持多層次或部件(parts)的概念。例如,你可以通過授予用戶權限來調整之前那個簡單的例子
```
printer:query
```
在這個例子中的冒號是一個特殊字符,它用來分隔權限字符串的下一部件。
在該例中,第一部分是權限被操作的領域(printer),第二部分是被執行的操作(query)。上面其他的例子將被改為:
```
printer:print
printer:manage
```
對于能夠使用的部件是沒有數量限制的,因此它取決于你的想象,依據你可能在你的應用程序中使用的方法
#### Multiple Values
每個部件能夠保護多個值。因此,除了授予用戶 "printer:print" 和 "printer:query" 權限外,你可以簡單地授予他們一個:
```
printer:print,query
```
它能夠賦予用戶 print 和query 打印機的能力。由于他們被授予了這兩個操作,你可以通過調用下面的語句來判斷用戶是否有能力查詢打印機:
```
subject.isPermitted("printer:query")
```
該語句將會返回true
#### All Values
如果你想在一個特定的部件給某一用戶授予所有的值呢?這將是比手動列出每個值更為方便的事情。同樣,基于通配符的話,我也可以做到這一點。若打印機域有3 個可能的操作(query,print 和manage),可以像下面這樣:
```
printer:query,print,manage
```
簡單點變成這樣:
```
printer:*
```
然后,任何對 "printer:XXX" 的權限檢查都將返回 true。以這種方式使用的通配符比明確地列出操作具有更好的尺度,如果你不久為應用程序增加了一個新的操作,你不需要更新使用通配符那部分的權限。
最后,在一個通配符權限字符串中的任何部分使用通配符 token 也是可以的。例如,如果你想對某個用戶在所有領域(不僅僅是打印機)授"view"權限,你可以這樣做:
```
*:view
```
這樣任何對"foo:view"的權限檢查都將返回true。
### Instance-Level Access Control
另一種常見的通配符權限用法是塑造實例級的訪問控制列表。在這種情況下,你使用三個部件——第一個是域,第二個是操作,第三個是被付諸實施的實例。
簡單例子
```
printer:query:lp7200
printer:print:epsoncolor
```
第一個定義了查詢擁有ID lp7200 的打印機的行為。第二條權限定義了打印到擁有 ID epsoncolor 的打印機的行為。
如果你授予這些權限給用戶,那么他們能夠在特定的實例上執行特定的行為。然后你可以在代碼中做一個檢查:
```
if ( SecurityUtils.getSubject().isPermitted("printer:query:lp7200") {
// 返回ID lp7200 的打印機的當前任務
}
```
這是體現權限的一個極為有效的方法。但同樣,為所有的打印機定義多個實例ID 能很好的擴展,尤其是當新的打印機添加到系統的時候。你可以使用通配符來代替:
```
printer:print:*
```
這個做到了擴展,因為它同時涵蓋了任何新的打印機。你甚至可以運行訪問所有打印機上的所有操作
```
printer:*:*
```
或在一臺打印機上的所有操作:
```
printer:*:lp7200
```
或甚至特定的操作:
```
printer:query, print:lp7200
```
`*`通配符,`,`子部件分離器可用于權限的任何部分。
#### Missing Parts 缺省的部分
最后要注意的是權限分配:缺省的部分意味著用戶可以訪問所有與之匹配的值,換句話說
```
printer:print
```
等價于
```
printer:print:*
```
并且
```
printer
```
等價于
```
printer:*:*
```
然而,你只能從字符串的結尾處省略部件,因此這樣的:
```
printer:lp7200
```
并不等價于
```
printer:*:lp7200
```
## Checking Permissions 檢查權限
雖然權限分配使用通配符較為方便且具有擴展性("printer:print:\*" = print to any printer),但在運行時的權限檢查應該始終基于大多數具體的權限字符串。
例如,如果用戶有一個用戶界面,他們想打印一份文檔到 lp7200 打印機,你應該通過執行這段代碼來檢查用戶是否被允許這樣做:
```
if ( SecurityUtils.getSubject().isPermitted("printer:print:lp7200") ) {
//用 lp7200 printer 打印文檔
}
```
這種檢查非常具體和明確地反映了用戶在那一時刻試圖做的事情。 然而,下面這個運行是檢查是不為理想的:
```
if ( SecurityUtils.getSubject().isPermitted("printer:print") ) {
//打印文檔
}
```
為什么?因為第二個例子表明“對于下面的代碼塊的執行,你必須能夠打印到任何打印機”。但請記住 "pinter:print" 是等價 "priner:print:\*" 的!
因此,這是一個不正確的檢查。如果當前用戶不具備打印到任何打印機的能力,僅僅只有打印到 lp7200 和 epsoncolor 的能力,該怎么辦呢?那么上面的第二個例子也絕不允許他們打印到 lp7200 打印機,即使他們已被賦予了相應的能力!
因此,經驗法則是在執行權限檢查時,盡可能使用權限字符串。當然,上面的第二塊可能是在應用程序中別處的一個有效檢查,如果你真的想要執行該代碼塊,如果用戶被允許打印到任何打印機(令人懷疑的,但有可能)。你的應用程序將決定檢查哪些有意義,但一般情況下,越具體越好。
## Implication, not Equality 蘊涵,不相等
為什么運行時權限檢查應該盡可能的具體,但權限分配可以較為普通?這是因為權限檢查是通過蘊含的邏輯來判斷的——而不是通過相等檢查。
也就是說,如果一個用戶被分配了 user: *權限,這意味著該用戶可以執行 user:view 操作。"user:*" 字符串明顯不等于 "user:view",但前者包含了后者。"user:\*" 描述了 "user:view" 所定義的功能的一個超集。
為了支持蘊含規則,所有的權限都被翻譯到實現org.apache.shiro.authz.Permission 接口的的對象實例中。這是以便 蘊含邏輯能夠在運行時執行,且蘊含邏輯通常比一個簡單的字符串相等檢查更為復雜。所有在本文檔中描述的通配符行為實際上是由org.apache.shiro.authz.permission.WildcardPermission 類實現的。下面是更多的一些通過蘊含邏輯訪問的通配符權限字符串:
```
user:*
```
同時蘊含著刪除一個用戶的能力:
```
user:delete
```
同樣地,
```
user:*:12345
```
同時蘊含著更新 ID 為12345 的用戶帳戶的能力:
```
user:update:12345
```
而且
```
printer
```
蘊含著打印到任何打印機的能力
```
printer:print
```
## Performance Considerations
權限檢查比簡單的相等比較要復雜得多,因此運行時的蘊含邏輯必須執行每個分配的權限。當使用像上面展示的權限字符串時,你正在隱式地使用 Shiro 默認的 WildcardPermission,它能夠執行必要的蘊含邏輯。
Shiro 對 Realm 實現的默認行為是,對于每一個權限驗證(例如,調用subject.isPermitted),所有分配給該用戶的權限(在他們的組,角色中,或直接分配給他們)需要為蘊含邏輯進行單獨的檢查。Shiro 通過首次成功檢查立即返回來“短路”該進程以提高性能,但它不是一顆銀彈。
這通常是極快的,當用戶,角色和權限緩存在內存中且使用了一個合適的[CacheManager](../VIII.%20Other%20%E5%85%B6%E4%BB%96/23.%20CacheManager%20%E7%BC%93%E5%AD%98%E7%AE%A1%E7%90%86.md) 時,在Shiro 不支持的 Realm 實現中。只要知道使用此默認行為,當權限分配給用戶或他們的角色或組增加時,執行檢查的時間一定會增加。
如果一個 Realm 的實現者有一個更為高效的方式來檢查權限并執行蘊含邏輯,尤其它如果是基于應用程序數據模型的,他們應該實現它作為 Realm isPermitted\* 方法實現的一部分。默認的 Realm/WildcardPermission 存在的支持覆蓋了大多數用例的80~90%,但它可能不是在運行時擁有大量權限需要存儲或檢查的應用程序的最佳解決方案。
## 為文檔加把手
我們希望這篇文檔可以幫助你使用 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 加密功能