shiro基于權限的授權
就像我們上面在角色概述中提到的,通過基于權限的授權執行訪問控制是更好的方法。基于權限的授權,因為其與程序功能(以及程序核心資源上的行為)緊密聯系,基于權限授權的源代碼在程序功能改變時才需要改變,而與安全策略無關。這意味著與同樣基于角色的授權相比,對代碼的影響更少。
1.權限檢查
如果你希望檢查一個Subject是否允許做某件事情,你可以調用isPermitted*方法的變形,有兩種主要方式檢查授權--基于對象的權限實例或者代表權限的字符串。
基于對象的權限檢查
執行權限檢查的一種方法是實例化一個Shiro的org.apache.shiro.authz.Permission接口并且將它傳遞給接收權限實例的*isPermitted方法。
例如,假設以下情景:辦公室里有一臺唯一標識為laserjet4400n的打印機,在我們向用戶顯示打印按鈕之前,軟件需要檢查當前用戶是否允許用這臺打印機打印文檔,檢查權限的方式會是這樣:
Permission printPermission = new PrinterPermission("laserjet4400n", "print");
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.isPermitted(printPermission)) {
//show the Print button
} else {
//don't show the button? Grey it out?
}
在這個例子中,我們同樣看到了一個非常強大的實例級別的訪問控制檢查--在單獨數據實例上限制行為的能力。
基于對象的權限對下列情況非常有用:
希望編譯期類型安全;
希望確保正確地引用和使用的權限;
希望對權限判斷邏輯(稱作權限隱含邏輯,基于權限接口的implies方法)執行進行明確控制;
希望確保權限正確地反映程序資源(例如,在一個對象域模型上創建一個對象時,權限類可能自動產生)。
下面是你可以根據需要調用的函數:
isPermitted(Permission p)
如果Subject允許執行特定權限實例綜合的動作或資源訪問返回真,否則返回假;
isPermitted(List perms)
按參數順序返回isPermitted的結果數組,如果許多權限需要檢查時非常有用(如定制一個復雜的視圖);
isPermittedAll(Collection perms)
如果Subject擁有指定的所有權限返回真,否則返回假。
基于字符串的權限檢查
雖然基于對象的權限檢查很有用(編譯期類型安全,對行為擔保,定制隱含邏輯等),但在許多程序里有時候感覺有點笨重,另一種選擇是用普通的字符串來代表權限實例。
例如,對于上面打印權限的例子,我們可以使用字符串權限檢查達到同樣的結果:
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.isPermitted("printer:print:laserjet4400n")) {
//show the Print button
} else {
//don't show the button? Grey it out?
}
這個例子同樣實現了實例級別的權限檢查,但是所有主要權限部件--printer(資源類型)、print(動作)、laserjet4400n(實例ID)都表現為一個字符串。
上面的例子展示了一種以冒號分割的特殊形式的字符串,定義于Shiro的 org.apache.shiro.authz.permission.WildcardPermission實現中,它適合大多數用戶的需求。
上面的代碼塊基本上是下面這段代碼的縮寫:
Permission p = new WildcardPermission("printer:print:laserjet4400n");
if (currentUser.isPermitted(p) {
//show the Print button
} else {
//don't show the button? Grey it out?
}
WildcardPermission令牌形式和構成選項將在Shiro的Permission文檔中深入討論。
上面的字符串使用默認的WildcardPermission格式,實際上你可以創造并使用你自己的字符串格式,我們將在下面Realm授權章節討論如何這樣做。
基于字符串的權限有利的一面在于你不需要實現一個接口而且簡單的字符串也非常易讀,而不利的一面在于不保證類型安全,而且當你需要定義超出字符串表現能力之外的更復雜的行為時,你仍舊希望基于權限接口實現你自己的權限對象。實際上,大部分Shiro的終端用戶回為其簡單而選擇基于字符串的方式,但最終你的程序需求決定了哪一種方法會更好。
和基于對象的權限檢查方法一樣,下面是字符串權限檢查的函數:
isPermitted(String perm)
如果Subject被允許執行字符串表達的動作或資源訪問權限,返回真,否則返回假;
isPermitted(String... perms)
按照參數順序返回isPermitted的結果數組,當許多字符串權限需要檢查時非常有用(如定制一個復雜的視圖時);
isPermittedAll(String... perms)
當Subject具備所有字符串定義的權限時返回真,否則返回假。
2.權限判斷
作為檢查Subject是否被允許做某件事之后的一個選擇,你可以在邏輯執行之前簡單判斷他們是否具備所需的權限,如果Subject不被允許,AuthorizationException異常被拋出,如果是允許的,判斷將安靜地執行并按期望順序執行下面的邏輯。
例如:
Subject currentUser = SecurityUtils.getSubject();
//guarantee that the current user is permitted
//to open a bank account:
Permission p = new AccountPermission("open");
currentUser.checkPermission(p);
openBankAccount();
或者,同樣的判斷,可以用字符串形式:
Subject currentUser = SecurityUtils.getSubject();
//guarantee that the current user is permitted
//to open a bank account:
currentUser.checkPermission("account:open");
openBankAccount();
與isPermitted* 方法相比較,這種方法的優勢是代碼更為清晰,如果當前Subject不符合條件,你不必創建你自己的AuthorizationExceptions異常(如果你不想那么做)。
下面是你可以根據需要調用的函數:
checkPermission(Permission p)
如果Subject被允許執行特定權限實例指定的動作或資源訪問,安靜地返回,否則拋出AuthorizationException異常。
checkPermission(String perm)
如果Subject被允許執行權限字符串指定的動作或資源訪問,安靜地返回,否則拋出AuthorizationException異常。
checkPermissions(Collection perms)
如果Subject被允許執行所有權限實例指定的動作或資源訪問,安靜地返回,否則拋出AuthorizationException異常。
checkPermissions(String... perms) 和上面的checkPermissions效果一樣,只是使用字符串權限類型。