上一節我們講了自定義Realm中的認證(doGetAuthenticationInfo),這節我們繼續講另一個方法doGetAuthorizationInfo授權
授權流程
流程如下:
- 首先調用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,其流程是:
1.1、如果調用hasRole,則直接獲取AuthorizationInfo.getRoles()與傳入的角色比較即可;
1.2、首先如果調用如isPermitted(“user:view”),首先通過PermissionResolver將權限字符串轉換成相應的Permission實例,默認使用WildcardPermissionResolver,即轉換為通配符的WildcardPermission;
2、通過AuthorizationInfo.getObjectPermissions()得到Permission實例集合;通過AuthorizationInfo. getStringPermissions()得到字符串集合并通過PermissionResolver解析為Permission實例;然后獲取用戶的角色,并通過RolePermissionResolver解析角色對應的權限集合(默認沒有實現,可以自己提供);
3、接著調用Permission. implies(Permission p)逐個與傳入的權限比較,如果有匹配的則返回true,否則false。
先看一段簡單的授權方法重寫
@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {//獲取用戶名String username = (String) principals.getPrimaryPrincipal();//此處從數據庫獲取該用戶的角色Set<String> roles = getRolesByUserName(username);//此處從數據庫獲取該角色的權限Set<String> permissions = getPermissionsByUserName(username);//放到info里返回SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.setStringPermissions(permissions);info.setRoles(roles);return info;}
復制代碼
PrincipalCollection
因為我們可以在Shiro中同時配置多個Realm,所以呢身份信息可能就有多個;因此其提供了PrincipalCollection用于聚合這些身份信息:
public?interface?PrincipalCollection?extends?Iterable,?Serializable?{??Object?getPrimaryPrincipal();?//得到主要的身份??<T>?T?oneByType(Class<T>?type);?//根據身份類型獲取第一個??<T>?Collection<T>?byType(Class<T>?type);?//根據身份類型獲取一組??List?asList();?//轉換為List??Set?asSet();?//轉換為Set??Collection?fromRealm(String?realmName);?//根據Realm名字獲取??Set<String>?getRealmNames();?//獲取所有身份驗證通過的Realm名字??boolean?isEmpty();?//判斷是否為空??
}???
復制代碼
因為PrincipalCollection聚合了多個,此處最需要注意的是getPrimaryPrincipal,如果只有一個Principal那么直接返回即可,如果有多個Principal,則返回第一個(因為內部使用Map存儲,所以可以認為是返回任意一個);oneByType / byType根據憑據的類型返回相應的Principal;fromRealm根據Realm名字(每個Principal都與一個Realm關聯)獲取相應的Principal。
AuthorizationInfo
AuthorizationInfo用于聚合授權信息的:
public?interface?AuthorizationInfo?extends?Serializable?{??Collection<String>?getRoles();?//獲取角色字符串信息??Collection<String>?getStringPermissions();?//獲取權限字符串信息??Collection<Permission>?getObjectPermissions();?//獲取Permission對象信息??
}???
復制代碼
當我們使用AuthorizingRealm時,如果身份驗證成功,在進行授權時就通過doGetAuthorizationInfo方法獲取角色/權限信息用于授權驗證。 Shiro提供了一個實現SimpleAuthorizationInfo,大多數時候使用這個即可。
我們再跟蹤一下代碼,看看是如何調用Authorizer的
subject.hasRole("admin")
復制代碼
- 調用DelegatingSubject類的hasRole方法
public boolean hasRole(String roleIdentifier) {return hasPrincipals() && securityManager.hasRole(getPrincipals(), roleIdentifier);}
復制代碼
- 調用AuthorizingSecurityManager的hasRole
public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {return this.authorizer.hasRole(principals, roleIdentifier);}
復制代碼
- AuthorizingSecurityManager類在創建的時候就注入了ModularRealmAuthorizer類為authorizer
public AuthorizingSecurityManager() {super();this.authorizer = new ModularRealmAuthorizer();}
復制代碼
- 繼續跟進到ModularRealmAuthorizer的hasRole方法
public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {assertRealmsConfigured();for (Realm realm : getRealms()) {if (!(realm instanceof Authorizer)) continue;if (((Authorizer) realm).hasRole(principals, roleIdentifier)) {return true;}}return false;}
復制代碼
- 此處的hasRole是調用AuthorizingRealm抽象類的hasRole方法。同理,isPermitted也是最后調用到此。
public boolean hasRole(PrincipalCollection principal, String roleIdentifier) {AuthorizationInfo info = getAuthorizationInfo(principal);return hasRole(roleIdentifier, info);}protected boolean hasRole(String roleIdentifier, AuthorizationInfo info) {return info != null && info.getRoles() != null && info.getRoles().contains(roleIdentifier);}public boolean isPermitted(PrincipalCollection principals, String permission) {Permission p = getPermissionResolver().resolvePermission(permission);return isPermitted(principals, p);}public boolean isPermitted(PrincipalCollection principals, Permission permission) {AuthorizationInfo info = getAuthorizationInfo(principals);return isPermitted(permission, info);}//changed visibility from private to protected for SHIRO-332protected boolean isPermitted(Permission permission, AuthorizationInfo info) {Collection<Permission> perms = getPermissions(info);if (perms != null && !perms.isEmpty()) {for (Permission perm : perms) {if (perm.implies(permission)) {return true;}}}return false;}
復制代碼