美文网首页Shiro
SpringBoot+Shiro学习(四):Realm授权

SpringBoot+Shiro学习(四):Realm授权

作者: Hiwayz | 来源:发表于2018-08-24 15:25 被阅读17次

    上一节我们讲了自定义Realm中的认证(doGetAuthenticationInfo),这节我们继续讲另一个方法doGetAuthorizationInfo授权

    授权流程

    image

    流程如下:

    1. 首先调用Subject.isPermitted/hasRole接口,其会委托给SecurityManager,而SecurityManager接着会委托给Authorizer
    2. Authorizer是真正的授权者,如果我们调用如isPermitted(“user:view”),其首先会通过PermissionResolver把字符串转换成相应的Permission实例;
    3. 在进行授权之前,其会调用相应的Realm获取Subject相应的角色/权限用于匹配传入的角色/权限;
    4. Authorizer会判断Realm的角色/权限是否和传入的匹配,如果有多个Realm,会委托给ModularRealmAuthorizer进行循环判断,如果匹配如isPermitted/hasRole会返回true,否则返回false表示授权失败。

    ModularRealmAuthorizer进行多Realm匹配流程:

    1. 首先检查相应的Realm是否实现了实现了Authorizer;
    2. 如果实现了Authorizer,那么接着调用其相应的isPermitted/hasRole接口进行匹配;
    3. 如果有一个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。


    先看一段简单的授权方法重写

        @Override
        protected 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

    image

    因为我们可以在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

    image

    AuthorizationInfo用于聚合授权信息的:

    public interface AuthorizationInfo extends Serializable {  
     Collection<String> getRoles(); //获取角色字符串信息  
     Collection<String> getStringPermissions(); //获取权限字符串信息  
     Collection<Permission> getObjectPermissions(); //获取Permission对象信息  
    }   
    

    当我们使用AuthorizingRealm时,如果身份验证成功,在进行授权时就通过doGetAuthorizationInfo方法获取角色/权限信息用于授权验证。
    Shiro提供了一个实现SimpleAuthorizationInfo,大多数时候使用这个即可。


    我们在跟踪一下代码,看看是如何调用Authorizer

    subject.hasRole("admin")
    
    1. 调用DelegatingSubject类的hasRole方法
        public boolean hasRole(String roleIdentifier) {
            return hasPrincipals() && securityManager.hasRole(getPrincipals(), roleIdentifier);
        }
    
    1. 调用AuthorizingSecurityManager的hasRole
     public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {
            return this.authorizer.hasRole(principals, roleIdentifier);
        }
    
    1. AuthorizingSecurityManager类在创建的时候就注入了ModularRealmAuthorizer类为authorizer
        public AuthorizingSecurityManager() {
            super();
            this.authorizer = new ModularRealmAuthorizer();
        }
    
    1. 继续跟进到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;
        }
    
    1. 此处的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-332
        protected 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;
        }
    

    相关文章

      网友评论

        本文标题:SpringBoot+Shiro学习(四):Realm授权

        本文链接:https://www.haomeiwen.com/subject/wrvbiftx.html