本文目錄:
► 第三章:Shiro授權-下
► 3.4 Permission
► 3.5 授權流程
下節預告
► 第四章:Shiro中Ini配置(預告)
► 4.1 SecurityManger根對象(預告)
► 4.2 ini配置(預告)
3.4 Permission
字符串通配符權限:
規則:資源標識符:操作:對象實例ID
注:即對哪個資源的哪個實例可以進行什麼操作
其默認支持通配符權限字符串:
“:”表示資源/操作/實例的分割;
“,”表示操作的分割;
“*” 表示任意資源/操作/實例。
1. 單個資源、單個權限
subject.checkPermissions("user:update");
2. 單個資源,多個權限
ini配置文件:
role4=user:update,user:delete
然後通過如下代碼判斷:
subject.checkPermissions("user:update", "user:delete");
用戶擁有資源“user”的“update”和“delete”權限,可以簡寫爲:
ini 配置(表示角色4擁有user 資源的 update 和 delete 權限)
role4="user:update,delete"
接着可以通過如下代碼判斷
subject.checkPermissions("user:update,delete");
通過“user:update,delete”驗證“user:update,user:delete”是沒問題的,但是反過來是規則不成立。
3. 單個資源、全部權限
Ini配置:
role5="user:create,update,delete,view"
然後通過如下代碼判斷:
subject.checkPermissions("user:create,delete,update:view");
用戶擁有資源“user”的“create”、“update”、“delete”和“view”所有權限。
如上可以簡寫成:
ini 配置文件(表示角色 5 擁有user 的所有權限)
role5=user:*
也可以簡寫爲(推薦上邊的寫法):
role5=user
然後通過如下代碼判斷:
subject.checkPermissions("user:*"); subject.checkPermissions("user");
通過“user:*”驗證“user:create,delete,update:view”可以,但是反過來是不成立的。
4. 所有資源、全部權限
ini 配置:
role6=*:view
然後通過如下代碼判斷:
subject.checkPermissions("user:view");
用戶擁有所有資源的“view”所有權限。假設判斷的權限是“"user:view”,那麼需要“role6=::view”這樣寫才行。
5. 實例級別的權限
a) 單個實例、單個權限
ini 配置:
role7=user:view:1
對資源 user 的 1 實例擁有 view 權限。
然後通過如下代碼判斷:
subject.checkPermissions("user:view:1");
b) 單個實例、多個權限
ini 配置:
role7="user:update,delete:1"
對資源 user 的 1 實例擁有 update、delete 權限。
然後通過如下代碼判斷 :
subject.checkPermissions("user:delete,update:1");
subject.checkPermissions("user:update:1", "user:delete:1");
c) 單個實例、所有權限
ini 配置:
role7=user:*:1
對資源 user 的 1 實例擁有所有權限。
然後通過如下代碼判斷:
subject.checkPermissions("user:update:1","user:delete:1","user:view:1");
d) 所有實例、單個權限
ini 配置:
role7=user:auth:*
對資源 user 的 1 實例擁有所有權限。
然後通過如下代碼判斷:
subject.checkPermissions("user:auth:1", "user:auth:2");
e) 所有實例、所有權限
ini 配置:
role7=user:*:*
對資源 user 的 1 實例擁有所有權限。
然後通過如下代碼判斷:
subject.checkPermissions("user:view:1", "user:auth:2");
6. Shiro對權限字符串缺失部分的處理
如“user:view”等價於“user:view:*”;
而“organization”等價於“organization:*”或者“organization:*:*”。
可以這麼理解,這種方式實現了前綴匹配。
另外如:
“user:*”可以匹配如“user:delete”
“user:delete”可以匹配如“user:delete:1”
“user:*:1”可以匹配如“user:view:1”、
“user”可以匹配”user:view”或“user:view:1”等。
- 即*可以匹配所有,不加*可以進行前綴匹配;
- 但是如“*:view”不能匹配“user:view”,需要使用“*:*:view”;
- 即後綴匹配必須指定前綴(多個冒號就需要多個*來匹配)。
7. WhildcardPermission
如下兩種方式是等價的:
subject.checkPermission("menu:view:1");
subject.checkPermission(new WildcardPermission("menu:view:1"));
8. 性能問題
通配符匹配方式比字符串相等匹配來說是更復雜的,因此需要花費更長時間,但是一般系統的權限不會太多,且可以配合緩存來提供其性能,如果這樣性能還達不到要求我們可以實現位操作算法實現性能更好的權限匹配。另外實例級別的權限驗證如果數據量太大也不建議使用,可能造成查詢權限及匹配變慢。可以考慮比如在sql查詢時加上權限字符串之類的方式在查詢時就完成了權限匹配。
3.5 授權流程
流程如下:
- 首先調用Subject.isPermitted*/hasRole* 接口,其會委託給 SecurityManager,而 SecurityManager 接着會委託給 Authorizer;
- Authorizer 是真正的授權者,如果我們調用如 isPermitted(“user:view”),其首先會通過 PermissionResolver 把字符串轉換成相應的 Permission 實例;
- 在進行授權之前,其會調用相應的 Realm 獲取 Subject 相應的角色/權限用於匹配傳入的角色/權限;
- Authorizer 會判斷 Realm 的角色/權限是否和傳入的匹配,如果有多個 Realm,會委託給 ModularRealmAuthorizer 進行循環判斷,如果匹配如isPermitted*/hasRole* 會返回 true,否則返回 false 表示授權失敗。
ModularRealmAuthorizer 進行多 Realm 匹配流程:
- 首先檢查相應的 Realm 是否實現了實現了 Authorizer;
- 如果實現了 Authorizer,那麼接着調用其相應的 isPermitted*/hasRole* 接口進行匹配;
- 如果有一個 Realm 匹配那麼將返回 true,否則返回 false。
如果 Realm 進行授權的話,應該繼承 AuthorizingRealm,其流程是:
- 如果調用 hasRole*,則直接獲取 AuthorizationInfo.getRoles() 與傳入的角色比較即可;首先如果調用如 isPermitted(“user:view”),首先通過 PermissionResolver 將權限字符串轉換成相應的 Permission 實例,默認使用 WildcardPermissionResolver,即轉換爲通配符的 WildcardPermission;
- 通過 AuthorizationInfo.getObjectPermissions() 得到 Permission 實例集合;通過 AuthorizationInfo.getStringPermissions() 得到字符串集合並通過 PermissionResolver 解析爲 Permission 實例;然後獲取用戶的角色,並通過 RolePermissionResolver 解析角色對應的權限集合(默認沒有實現,可以自己提供);
- 接着調用 Permission.implies(Permission p) 逐個與傳入的權限比較,如果有匹配的則返回 true,否則 false。
Authorizer、PermissionResolver及RolePermissionResolver:
Authorizer 的職責是進行授權(訪問控制),是 Shiro API 中授權核心的入口點,其提供了相應的角色/權限判斷接口,具體請參考其 Javadoc。SecurityManager 繼承了 Authorizer 接口,且提供了 ModularRealmAuthorizer 用於多 Realm 時的授權匹配。
PermissionResolver 用於解析權限字符串到 Permission 實例。
RolePermissionResolver 用於根據角色解析相應的權限集合。
我們可以通過如下 ini 配置更改 Authorizer 實現:
authorizer=org.apache.shiro.authz.ModularRealmAuthorizer securityManager.authorizer=$authorizer
對於 ModularRealmAuthorizer,相應的 AuthorizingSecurityManager 會在初始化完成後自動將相應的 realm 設置進去,我們也可以通過調用其 setRealms() 方法進行設置。對於實現自己的 authorizer 可以參考 ModularRealmAuthorizer 實現即可。
設置 ModularRealmAuthorizer 的 permissionResolver,其會自動設置到相應的 Realm 上(其實現了 PermissionResolverAware 接口),如:
permissionResolver=org.apache.shiro.authz.permission.WildcardPermissionResolver authorizer.permissionResolver=$permissionResolver
設置 ModularRealmAuthorizer 的 rolePermissionResolver,其會自動設置到相應的 Realm 上(其實現了 RolePermissionResolverAware 接口),如:
rolePermissionResolver=com.ms.MyRolePermissionResolver authorizer.rolePermissionResolver=$rolePermissionResolver