Shiro权限验证最详细的代码流转分析

Shiro权限验证

1.测试类


首先调用

subject().isPermitted("user1:update");

subject会委托给SecurityManager

而SecurityManager接着会委托给Authorizer;

Authorizer是真正的授权者

Authorizer是个接口,会接着调用它实现类(ModularRealmAuthorizer)的isPermitted方法去做认证

循环验证是否权限匹配可以看出只要有一个权限验证通过即返回为true

public boolean isPermitted(PrincipalCollection principals, String permission) {
        assertRealmsConfigured();
        for (Realm realm : getRealms()) {
            if (!(realm instanceof Authorizer)) continue;
            if (((Authorizer) realm).isPermitted(principals, permission)) {
                return true;
            }
        }
        return false;
    }
其中

assertRealmsConfigured方法源码为:其作用主要判断realm是否为空(这个方法蛮好的,学学)

protected void assertRealmsConfigured() throws IllegalStateException {
        Collection<Realm> realms = getRealms();
        if (realms == null || realms.isEmpty()) {
            String msg = "Configuration error:  No realms have been configured!  One or more realms must be " +
                    "present to execute an authorization operation.";
            throw new IllegalStateException(msg);
        }
    }

此时上面哪个isPermitted(principals, permission)方法调用其实现接口(Authorizer)的方法isPermitted(principals, permission)

进而调用其实现类(AuthorizingRealm)的方法

首先对传进来的要验证的权限进行解析

用开发人员指定的解析器BitAndWildPermissionResolver 

public boolean isPermitted(PrincipalCollection principals, String permission) {
        Permission p = getPermissionResolver().resolvePermission(permission);
        return isPermitted(principals, p);
    }

用自定义的权限解析器去把字符串的权限转换成Permission类型的权限

public class BitAndWildPermissionResolver implements PermissionResolver {

    public Permission resolvePermission(String permissionString) {
        //以+号开始的都走自定义的权限解析器
        if(permissionString.startsWith("+")){
            return new BitPermission(permissionString);
        }
        //其余的走shiro,默认的通配符权限解析器
        return new WildcardPermission(permissionString);
    }

}


然后对传入的权限进行比较

public boolean isPermitted(PrincipalCollection principals, Permission permission) {
        AuthorizationInfo info = getAuthorizationInfo(principals);
        return isPermitted(permission, info);
    }
次方法作用:

    (0)首先从缓存中取权限信息,如果没有才去realm里面取

Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache();

    (1)首先从自定义的MyRealm的类中获取权限集合(原始的,未解析的,包括字符串,wild,bit各种类型的初始权限参数)

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

        authorizationInfo.addRole("role1");
        authorizationInfo.addRole("role2");
        authorizationInfo.addObjectPermission(new BitPermission("+user1+10"));
        authorizationInfo.addObjectPermission(new WildcardPermission("user1:*"));
        authorizationInfo.addStringPermission("+user2+10");
        authorizationInfo.addStringPermission("user2:*");

        return authorizationInfo;
    }


注意:关于缓存的问题

if (info == null) {
            // Call template method if the info was not found in a cache
            info = doGetAuthorizationInfo(principals);
            // If the info is not null and the cache has been created, then cache the authorization info.
            if (info != null && cache != null) {
                if (log.isTraceEnabled()) {
                    log.trace("Caching authorization info for principals: [" + principals + "].");
                }
                Object key = getAuthorizationCacheKey(principals);
                cache.put(key, info);
            }
        }

这里有判断默认的cache实现,如果你shiro中配置了缓存就会把当前权限信息缓存在缓存其中

   

  (2)进行权限的实际比较(解析权限字符串)

return isPermitted(permission, info);
关键时刻到了:

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

其中黄底蓝字的地方会把刚刚从自定义realm里面取得的权限信息(良莠不齐)转化成统一的Permission对象来比较

最终解析出来的权限如下(包括直接赋予的权限,以及拥有角色的所拥有的权限)



准备工作完成(要验证的权限,用户实际拥有的权限

可以开始比较了possessPermissions

Collection<Permission> possessPermissions= getPermissions(info);
        if (possessPermissions!= null && !possessPermissions.isEmpty()) {
            for (Permission perm : possessPermissions) {
                if (perm.implies(wantTocheckPermissions)) {
                    return true;
                }
            }
        }
        return false;
有一个返回true则为true





web.xml配置 因为我们是与spring进行集成的,而spring的基本就是web项目的xml文件。所以我们在web.xml中配置shiros的过滤拦截。正常情况下,我们需要将shiro的filter配置在所有的filter前面,当然和encodingFilter这个filter是不区分前后的。因为两者互相不影响的。spring-shiro.xml 这里我们将来看看spring-shiro.xml的配置,这里我采取倒叙的方式讲解,我觉的倒叙更加的有助于我们理解代码。首先我们还记得在web.xml中配置的那个filter吧,名字shiroFilter,对spring-shiro.xml配置文件就是通过这个filter展开的。首先我们在web.xml配置的过滤器实际上是配置ShiroFilterFactoryBean,所以在这里需要将ShiroFilterFactoryBean定义为shiroFilter <!-- Shiro的核心安全接口,这个属性是必须的 --> <!-- 要求登录时的链接(可根据项目的URL进行替换),非必须的属性,默认会自动寻找Web工程根目录下的"/login.html"页面 --> <!-- 登录成功后要跳转的连接 --> <!-- 用户访问未对其授权的资源时,所显示的连接 --> <!-- 若想更明显的测试此属性可以修改它的值,如unauthor.jsp,然后用[玄玉]登录后访问/admin/listUser.jsp就看见浏览器会显示unauthor.jsp --> <!-- Shiro连接约束配置,即过滤链的定义 --> <!-- 此处可配合我的这篇文章来理解各个过滤连的作用http://blog.csdn.net/jadyer/article/details/12172839 --> <!-- 下面value值的第一个'/'代表的路径是相对于HttpServletRequest.getContextPath()的值来的 --> <!-- anon:它对应的过滤器里面是空的,什么都没做,这里.do和.jsp后面的*表示参数,比方说login.jsp?main这种 --> <!-- authc:该过滤器下的页面必须验证后才能访问,它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter --> /statics/**=anon /login.html=anon /sys/schedule.html=perms[sys:schedule:save] /sys/login=anon /captcha.jpg=anon /**=authc
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

花生糖葫芦侠

创作不易,请多多支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值