Shiro 学习笔记(5)—— 自定义权限解析器和角色解析器

Shiro 学习笔记(5)—— 自定义角色权限解析器

执行授权代码的前提

我们首先要明确的一个知识点是,什么时候 Shiro 才会去执行权限匹配的代码呢?
(1)在我们的 Realm 的实现类中,必须要有关于 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) 方法的实现;
(2)在调用 Shiro 的认证成功以后的 isPermitted() 方法或者 checkPermission() 方法的时候。这一点很容易看出,当我们检查权限的时候,就会去执行授权的逻辑。

自定义权限解析规的步骤

步骤1:实现 Permission 接口

在这个接口中实现权限匹配的方法。

public class MyPermission implements Permission {

    private String resourceId;
    private String operator;
    private String instanceId;

    // 这里为了说明问题,省略了 get 和 set 方法
    // 同时 get 和 set 方法只会在这个类的内部使用,所以其实没有必要设置和对外开放
    // 调用方法也会消耗更多内存

    public MyPermission(){

    }

    public MyPermission(String permissionStr){
        String[] strs = permissionStr.split("\\+");
        if(strs.length>1){
            this.resourceId = strs[1];
        }
        if(this.resourceId == null || "".equals(this.resourceId)){
            this.resourceId = "*";
        }
        if(strs.length>2){
            this.operator = strs[2];
        }
        if(strs.length>3){
            this.instanceId = strs[3];
        }
        if(this.instanceId == null || "".equals(this.instanceId)){
            this.instanceId = "*";
        }

        System.out.println("实例化 MyPermission 时 => " + this.toString());
    }


    /**
     * 【这是一个非常重要的方法】
     * 由程序员自己编写授权是否匹配的逻辑,
     * 我们这里的实现,是将 Realm 中给出的 Permission 和 ini 配置中指定的 PermissionResoler 中指定的 Permission 进行比对
     * 比对的规则完全由我们自己定义
     * @param permission
     * @return
     */
    @Override
    public boolean implies(Permission permission) {
        if(!(permission instanceof MyPermission)){
            return false;
        }
        MyPermission mp = (MyPermission)permission;

        if(!"*".equals(mp.resourceId) && !this.resourceId.equals(mp.resourceId)){
            return false;
        }
        if(!"*".equals(mp.operator) && !this.operator.equals(mp.operator)){
            return false;
        }
        if(!"*".equals(mp.instanceId) && !this.instanceId.equals(mp.instanceId)){
            return false;
        }
        return true;
    }


    @Override
    public String toString() {
        return "MyPermission{" +
                "resourceId='" + resourceId + '\'' +
                ", operator='" + operator + '\'' +
                ", instanceId='" + instanceId + '\'' +
                '}';
    }
}

步骤2:实现 PermissionResolver 接口

实现 PermissionResolver 接口的意义在于告诉 Shiro 根据字符串的表现形式(表现特征),采用什么样的 Permission 进行匹配。

代码:

public class MyPermissionResolver implements PermissionResolver{
    @Override
    public Permission resolvePermission(String s) {
        if(s.startsWith("+")){
            return new MyPermission(s);
        }
        return new WildcardPermission(s);
    }
}

说明:这段代码实现的一个效果是:如果我们的权限字符串是以 “+” 号开头的话,就使用我们自定义的 MyPermission ,否则就使用默认的 WildcardPermission 解析这个字符串。

步骤3:实现授权的逻辑

这里,我们还是使用刚刚用的 StaticRealm 去实现授权操作,代码:

public class MyStaticRealm extends AuthorizingRealm {

    /**
     * 用于授权
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("static Realm 中授权的方法");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addRole("r1");
        info.addRole("r2");
        info.addStringPermission("+user+");
        info.addObjectPermission(new MyPermission("+user+add+1"));
        return info;
    }

    /**
     * 用于认证
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 省略,同之前的例子
    }
}

步骤 4:在 shiro.ini 文件中增加配置

permissionResolver=com.liwei.permission.MyPermissionResolver
authorizer.permissionResolver=$permissionResolver
securityManager.authorizer=$authorizer

详细说明代码执行流程

代码其实就是上面的样子,但是光这么讲很抽象,此时权限匹配的过程是定义在我们实现的 Permission 接口的 implies() 方法中。所以我们还要详细介绍 Shiro 在进行权限匹配的流程:

1、首先调用 Subject.isPermitted() 接口,其会委托给 SecurityManager,而 SecurityManager 接着会委托给 Authorizer

2、Authorizer 是真正的授权者,

如果我们调用如 isPermitted(“user:view”),其首先会通过 PermissionResolver 把字符串转换成相应的 Permission 实例。下面我们说明这个实例是如何转换的呢?

根据我们在 shiro.ini 文件中配置的

permissionResolver=com.liwei.permission.MyPermissionResolver
authorizer.permissionResolver=$permissionResolver
securityManager.authorizer=$authorizer

此时 Shiro 会去这个配置文件中指定的 PermissionResolver 找相应的实现类 MyPermissionResolver ,通过“user:view”这个字符串去实现类中找到对应应该使用的 Permission 实现类,然后实例化一个 Permission 对象(这个 Permission 对象很重要,我们暂时把它记住,下一步马上就要用来做匹配)。

3、在进行授权之前,其会调用相应的 Realm 获取 Subject 相应的角色/权限用于匹配传入的角色/权限;

这个转换实例的过程就是在代码中通过在 Realm 中实现的 doGetAuthorizationInfo() 方法里写的 info.addObjectPermission(new MyPermission(“+user+add+1”)); 即构造函数实现的,在这个过程中也实例化了一个 Permission 对象,用于和上一步的 Permission 对象进行比较,这个比较的过程是通过我们实现 Permission 接口中的 implies() 方法来定义的 。

如果权限的字符串表示是一个集合,那么 implies() 方法可能会被执行多次,直到该方法返回 ‘true’ 为止。

4、Authorizer 会判断 Realm 的角色/权限是否和传入的匹配,如果有多个 Realm,会委托给 ModularRealmAuthorizer 进行循环判断,如果匹配,那么 isPermitted*/hasRole* 会返回 true,否则返回 false ,表示授权验证失败。

自定义角色匹配器

下面继续说明如何自定义角色匹配,其实过程和自定义权限匹配是类似的,但是我们要明确一点,因为角色是一组权限的集合,所以我们指定角色解析器的时候,须要告诉 Shiro 使用什么 Permission 。

步骤1:实现 RolePermissionResolver 接口

public class MyRolePermissionResolver implements RolePermissionResolver {
    @Override
    public Collection<Permission> resolvePermissionsInRole(String s) {
        if(s.contains("role1")){
            return Arrays.asList((Permission) new MyPermission("+user+add+1"));
        }
        return null;
    }
}

步骤2:在 shiro.ini 中配置我们的 RolePermissionResolver

authorizer=org.apache.shiro.authz.ModularRealmAuthorizer

rolePermissionResolver=com.liwei.role.MyRolePermissionResolver
authorizer.rolePermissionResolver=$rolePermissionResolver

permissionResolver=com.liwei.permission.MyPermissionResolver
authorizer.permissionResolver=$permissionResolver

securityManager.authorizer=$authorizer
securityManager.realms=$myStaticRealm

步骤3:在自定义的 Realm 的授权方法中添加角色

public class MyStaticRealm extends AuthorizingRealm {

    /**
     * 用于授权
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("static Realm 中授权的方法");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addRole("role1");
        return info;
    }

    /**
     * 用于认证
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 省略
    }
}

步骤 4 :判断用户是否具有授权方法中指定角色的权限

代码片段:

currentSubject.checkPermission("+user+add+1");

(终于写完了,不知道能不能讲清楚这件事情,大家光看也是很难理解的,须要自己动手练习,多打出一些帮助理解的代码信息就可以很快理解了)。

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值