Shiro安全登录、权限授权源码解读(下)

前言

  围绕上篇文章(地址:http://blog.csdn.net/fjekin/article/details/79141843),这节来看下shiro是如何实现权限授权的?

Coding

    /**
     * 权限认证
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        IShiro shiroFactory = ShiroFactroy.me();
        ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
        List<Long> roleList = shiroUser.getRoleList();

        Set<String> permissionSet = new HashSet<>();
        Set<String> roleNameSet = new HashSet<>();

        for (Long roleId : roleList) {
            List<String> permissions = shiroFactory.findPermissionsByRoleId(roleId);
            if (permissions != null) {
                for (String permission : permissions) {
                    if (ToolUtil.isNotEmpty(permission)) {
                        permissionSet.add(permission);
                    }
                }
            }
            String roleName = shiroFactory.findRoleNameByRoleId(roleId);
            roleNameSet.add(roleName);
        }

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermissions(permissionSet);
        info.addRoles(roleNameSet);
        return info;
    }

在自定义Realm的doGetAuthorizationInfo方法中,可以看出,通过PrincipalCollection获取shiro存储对象,再通过信息查询该对象相关内容,最后存放到SimpleAuthorizationInfo中。那么, PrincipalCollection是什么呢?存储到SimpleAuthorizationInfo对象中的权限信息又是如何跟前台访问信息如何进行校验呢?
这里写图片描述
通过查看继承关系(快捷方式:选中名称+F4),实现类SimplePrincipalCollection通过认证时候存储的SimpleAuthenticationInfo,以集合方式获取到存储的ShiroUser对象。

    /**
     * Returns the first available principal from any of the {@code Realm} principals, or {@code null} if there are
     * no principals yet.
     * <p/>
     * The 'first available principal' is interpreted as the principal that would be returned by
     * <code>{@link #iterator() iterator()}.{@link java.util.Iterator#next() next()}.</code>
     *
     * @inheritDoc
     */
    public Object getPrimaryPrincipal() {
        if (isEmpty()) {
            return null;
        }
        return iterator().next();
    }

权限方面,首先必定是先校验是否同个用户,其次再判断是否有请求地址权限。
通过调用org.apache.shiro.subject.Subject.isPermitted(String permission)接口,委托给SecurityManager,而SecurityManager接着委托给Authorizer。

public class DelegatingSubject implements Subject {

    private static final Logger log = LoggerFactory.getLogger(DelegatingSubject.class);

    private static final String RUN_AS_PRINCIPALS_SESSION_KEY =
            DelegatingSubject.class.getName() + ".RUN_AS_PRINCIPALS_SESSION_KEY";

    protected PrincipalCollection principals;
    protected boolean authenticated;
    protected String host;
    protected Session session;
    /**
     * @since 1.2
     */
    protected boolean sessionCreationEnabled;

    protected transient SecurityManager securityManager;

    ......
    public boolean isPermitted(Permission permission) {
        return hasPrincipals() && securityManager.isPermitted(getPrincipals(), permission);
    }

    public boolean[] isPermitted(String... permissions) {
        if (hasPrincipals()) {
            return securityManager.isPermitted(getPrincipals(), permissions);
        } else {
            return new boolean[permissions.length];
        }
    }

    public boolean[] isPermitted(List<Permission> permissions) {
        if (hasPrincipals()) {
            return securityManager.isPermitted(getPrincipals(), permissions);
        } else {
            return new boolean[permissions.size()];
        }
    }
    ......

}
public interface SecurityManager extends Authenticator, Authorizer, SessionManager

Authorizer是真正的授权者,而它的实现类,就是我们自定义Realm的父类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);
    }

    //visibility changed from private to protected per 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;
    }

PermissionResolver解析器通过resolvePermission()把字符串转换成Permission实例,再通过PrincipalCollection获取对应的AuthorizationInfo用户信息,而该对象存储权限信息就是我们在权限认证部分set进去的(本质就是set和get),最后通过implies()匹配。
通过继承关系,可以看出Permission中implies()具体实现是在父类WildcardPermission,实际上就是以集合包含关系方式来做判断。

    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) {
            // If this permission has less parts than the other permission, everything after the number of parts contained
            // in this permission is automatically implied, so return true
            if (getParts().size() - 1 < i) {
                return true;
            } else {
                Set<String> part = getParts().get(i);
                if (!part.contains(WILDCARD_TOKEN) && !part.containsAll(otherPart)) {
                    return false;
                }
                i++;
            }
        }

        // If this permission has more parts than the other parts, only imply it if all of the other parts are wildcards
        for (; i < getParts().size(); i++) {
            Set<String> part = getParts().get(i);
            if (!part.contains(WILDCARD_TOKEN)) {
                return false;
            }
        }

        return true;
    }

Ending

无论是登录还是权限,大体上都是跟平时处理的思维方式一致,只不过shiro把它封装起来而已。因此,总结出一点,在看源码的时候,最好是先思考一下,如果让你去实现的话,会怎么做?一来,会有种思维方式引导着你,慢慢探索,有种豁然开朗、解密的感觉;其次,可以吸收代码设计模式。
文章如有笔误,请及时留言相告!!!欢迎加群一起学习,QQ:583138104
相关代码下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值