shiro的授权流程,源码分析

一切的源头都是从subject开始,所以,打开看看

首先有两个概念需要搞清楚

隐式角色:直接通过身份验证是否有权限

显示角色:在程序中通过权限控制谁能访问某个资源

shiro的权限设计有很多种

RBAC所代表的是对角色进行控制,即只控制资源与角色之间的关系,并且一般来说这里的资源粒度只是细化到页面,但是对于一些要求更加细致的权限控制(控制到按钮)单纯的架构层面就无法满足,此时就需要程序员对页面进行处理,架构层面的需要后期处理就说明设计模式有所欠缺。

RBAC新解所代表的是粒度比RBAC对资源划分更加细化,它直接对访问资源的任何操作做权限控制
参看博文:https://blog.csdn.net/xk147258/article/details/100701256

这里我们只看在源码中,shiro的授权流程是怎样的,下面是subject中所有关于权限验证的方法

//判断是否拥有权限
boolean isPermitted(String permission);
//传入权限通配符,判断对某个资源可以进行某些操作
boolean isPermitted(Permission permission);
//这里可以一次性传入权限组来验证是否拥有权限
boolean[] isPermitted(String... permissions);

boolean[] isPermitted(List<Permission> permissions);
//传入权限组判断是否拥有所有权限
boolean isPermittedAll(String... permissions);

boolean isPermittedAll(Collection<Permission> permissions);

void checkPermission(String permission) throws AuthorizationException;

void checkPermission(Permission permission) throws AuthorizationException;

void checkPermissions(String... permissions) throws AuthorizationException;

void checkPermissions(Collection<Permission> permissions) throws AuthorizationException;

boolean hasRole(String roleIdentifier);

boolean[] hasRoles(List<String> roleIdentifiers);

boolean hasAllRoles(Collection<String> roleIdentifiers);

void checkRole(String roleIdentifier) throws AuthorizationException;

void checkRoles(Collection<String> roleIdentifiers) throws AuthorizationException;

void checkRoles(String... roleIdentifiers) throws AuthorizationException;

现在使用的比较多的是基于资源的访问控制即显示角色,这种策略的意思是譬如某个资源为user,这个user的所有find,update,insert,delete操作都归由shiro管理,至于隐式角色大家可以参考quick4j的开源项目

所以我们这里只看和Permission有关的方法

sunbject依然是交给securitymanager进行授权

 

 

 然后securitymanager委托给Authorizer

 

 

 找到实现方法在AuthorizingRealm中

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;
    }

这段代码的意思我尽量写详细点

PrincipalCollection:subject里的主题集合,
permission:传入的权限字符串,或permission实例
如果传入的是字符串,会先通过PermissionResolver将字符串转换为permission实例
看看实现过程

 

 

 起作用的是WildcardPermission中的setparts

protected void setParts(String wildcardString, boolean caseSensitive) {
        wildcardString = StringUtils.clean(wildcardString);

        if (wildcardString == null || wildcardString.isEmpty()) {
            throw new IllegalArgumentException("Wildcard string cannot be null or empty. Make sure permission strings are properly formatted.");
        }
       //判断传入的是否是字符串,是则转换为小写(总感觉老外的思维比较奇特。这个caseSensitive默认值是true这里还做个判断,一开始就对参数个数做了限制,还要做个反向判断)
        if (!caseSensitive) {
            wildcardString = wildcardString.toLowerCase();
        }
        //根据,分割字符串
        List<String> parts = CollectionUtils.asList(wildcardString.split(PART_DIVIDER_TOKEN));
        //封装为set集合
        this.parts = new ArrayList<Set<String>>();
        for (String part : parts) {
            Set<String> subparts = CollectionUtils.asSet(part.split(SUBPART_DIVIDER_TOKEN));

            if (subparts.isEmpty()) {
                throw new IllegalArgumentException("Wildcard string cannot contain parts with only dividers. Make sure permission strings are properly formatted.");
            }
            this.parts.add(subparts);
        }

        if (this.parts.isEmpty()) {
            throw new IllegalArgumentException("Wildcard string cannot contain only dividers. Make sure permission strings are properly formatted.");
        }
    }

转换为permission实例后,需要对应的realm根据subject即principals获取AuthorizationInfo,看看获取过程

 

 

 

 shiro默认都是从缓存中获取信息,所以我们的权限系统都需要自定义这些东西

 最后将permission实例和AuthorizationInfo传入permiss中的implies方法逐个对比,这个方法有点不容易看懂

 public boolean implies(Permission p) {
        // By default only supports comparisons with other WildcardPermissions
        if (!(p instanceof WildcardPermission)) {
            return false;
        }

        WildcardPermission wp = (WildcardPermission) p;

        List<Set<String>> otherParts = wp.getParts();

        int i = 0;
        for (Set<String> otherPart : otherParts) {           //如果传入的permission实例长度小于info的长度,通过
            if (getParts().size() - 1 < i) {
                return true;
            } else {
//如果传入的permision后缀不是*,且info不是传入permission的子集,就是内容不一致返回false
                Set<String> part = getParts().get(i);
                if (!part.contains(WILDCARD_TOKEN) && !part.containsAll(otherPart)) {
                    return false;
                }
                i++;
            }
        }

        // 如果多出来的part不是*不通过
        for (; i < getParts().size(); i++) {
            Set<String> part = getParts().get(i);
            if (!part.contains(WILDCARD_TOKEN)) {
                return false;
            }
        }

        return true;
    }

参考博文https://blog.csdn.net/sharetop/article/details/50250651

匹配完成返回true,false为失败

 

posted @ 2020-04-26 16:37  BrightFl  阅读(488)  评论(0编辑  收藏  举报