Shrio + CAS 学习笔记——权限

数据库中相关的表

sys_organization:组织表

基本字段:主键id、组织名称name、排序sort、状态state(0禁用、1启用)
其他字段:创建时间createTime、部门层级level、父id parentId、父级名称parentName、备注remark、更新时间updateTime

sys_user:用户表

基本字段:主键id、用户名username、状态state(0禁用、1启用、2锁定)
其他字段:创建时间createTime、名称nickName、组织主键orgId、组织名称orgName、电话phone、角色主键roleId、角色名称roleName、更新时间updateTime

猜测:基本字段中没有盐值salt、密码password是因为整合了CAS实现了单点登陆

sys_role:角色表

基本字段:主键id、角色唯一编码code(需遵守规范,@RequiresRoles中校验)、角色名称name、状态state(0禁用、1启用)
其他字段:创建时间createTime、备注remark、类型type(不明)

sys_permission:权限表

基本字段:主键id、唯一编码code、层级level、权限名称name、
其他字段:创建时间createTime、图标icon、父id parentId、备注remark、排序sort、状态state(0禁用、1启用)、类型type(1菜单、2按钮)、更新时间updateTime、路径url

sys_role_permission:角色-权限表

基本字段:主键id、权限id permissionId、角色id roleId、状态state(0禁用、1启用)
其他字段:创建时间createTime、备注remark、更新时间updateTime

sys_user_role:用户-角色表

未出现
猜测:sys_user中存在字段roleId,代替了用户角色表的作用

code惯例

资源:操作:实例
user:update:1

可以根据需求自己定义,上面只是Shrio给出的字符串惯例

其中有三个符号:
*星号(通配符,代表所有权限/角色)
:冒号(层级关系,user:update 包裹着 user:update:1)
,逗号(与的关系,而非或,user:add,update:1 代表对1的add和update权限都需要)

权限/角色授权、校验流程

用户授权 doGetAuthorizationInfo

  1. 获取当前用户名称
String userName = (String) principals.getPrimaryPrincipal();
  1. 创建 SimpleAuthorizationInfo(Simple POJO implementation of the AuthorizationInfo interface that stores roles and permissions as internal attributes.)
SimpleAuthorizationInfo auth = new SimpleAuthorizationInfo();
  1. 查询角色表和权限表中对应数据,set 到 auth 中
auth.setRoles(roles);
auth.setStringPermissions(permssions);

第一次访问需要权限的请求(被@RequiresPermissions、@RequiresRoles标注)会通过doGetAuthorizationInfo获取用户角色和权限,然后将其存入缓存,当前会话结束之前无需重复访问doGetAuthorizationInfo获取。

在这里插入图片描述

校验权限

@RequiresPermissions源码👇

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresPermissions {

	// 字符串权限数组,例 ["user:add", "user:update:1"]
    String[] value();

	// 逻辑,默认是AND
    Logical logical() default Logical.AND; 

}

SecurityUtils.getSubject().isPermitted(“user:add”) 的作用与 @RequiresPermissions(“user:add”) 一致
@RequiresPermissions(“user:add”) 触发最后的 implies 方法好像是通过切面,还不太清楚…

Subject 接口👇

public interface Subject {
	// 还有很多方法这里不一一列举
	boolean isPermitted(String permission);
	
	boolean isPermitted(Permission permission);
}

DelegatingSubject 类实现了 Subject 接口👇

public class DelegatingSubject implements Subject {
	// 展示部分相关源码
	protected transient SecurityManager securityManager;
	
	public boolean isPermitted(String permission) {
        return hasPrincipals() && securityManager.isPermitted(getPrincipals(), permission);
    }

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

SecurityManager 接口继承了 Authorizer 接口👇

public interface SecurityManager extends Authenticator, Authorizer, SessionManager {
	// 省略源码
}

因此在 DelegatingSubject 中的 isPermitted 方法即为 Authorizer 中 isPermitted 方法

public interface Authorizer {
	// 同样只展示部分相关源码
	boolean isPermitted(PrincipalCollection principals, String permission);

    boolean isPermitted(PrincipalCollection subjectPrincipal, Permission permission);
}

而 AuthorizingRealm 类实现了 Authorizer 为 isPermitted 提供具体实现

public abstract class AuthorizingRealm extends AuthenticatingRealm
        implements Authorizer, Initializable, PermissionResolverAware, RolePermissionResolverAware {
    // 省略源码
}

在 AuthorizingRealm 类中三个 isPermitted 方法:

  1. isPermitted(PrincipalCollection principals, String permission)
	// 将 String 类型的字符串权限装配成 Permission 类型
    public boolean isPermitted(PrincipalCollection principals, String permission) {
        Permission p = getPermissionResolver().resolvePermission(permission);
        return isPermitted(principals, p);
    }

PermissionResolver 接口中存在 resolvePermission 方法👇

public interface PermissionResolver {
    Permission resolvePermission(String permissionString);
}

WildcardPermissionResolver 实现了 PermissionResolver 接口👇

public class WildcardPermissionResolver implements PermissionResolver {
    public Permission resolvePermission(String permissionString) {
    	// WildcardPermission 实现了 Permission 接口
        return new WildcardPermission(permissionString);
    }
}

WildcardPermission 部分相关源码👇
通过 setParts 方法处理为 String 类型的权限

public class WildcardPermission implements Permission, Serializable {
    protected static final String WILDCARD_TOKEN = "*";
    protected static final String PART_DIVIDER_TOKEN = ":";
    protected static final String SUBPART_DIVIDER_TOKEN = ",";
    protected static final boolean DEFAULT_CASE_SENSITIVE = false;

    private List<Set<String>> parts;

    protected WildcardPermission() {
    }

    public WildcardPermission(String wildcardString) {
        this(wildcardString, DEFAULT_CASE_SENSITIVE);
    }

    public WildcardPermission(String wildcardString, boolean caseSensitive) {
        setParts(wildcardString, caseSensitive);
    }

    protected void setParts(String wildcardString, boolean caseSensitive) {
        if (wildcardString == null || wildcardString.trim().length() == 0) {
            throw new IllegalArgumentException("Wildcard string cannot be null or empty. Make sure permission strings are properly formatted.");
        }

        wildcardString = wildcardString.trim();

        List<String> parts = CollectionUtils.asList(wildcardString.split(PART_DIVIDER_TOKEN));

        this.parts = new ArrayList<Set<String>>();
        for (String part : parts) {
            Set<String> subparts = CollectionUtils.asSet(part.split(SUBPART_DIVIDER_TOKEN));
            if (!caseSensitive) {
                subparts = lowercase(subparts);
            }
            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.");
        }
    }
}
  1. isPermitted(PrincipalCollection principals, Permission permission)
	// 获取授权时存的权限信息
    public boolean isPermitted(PrincipalCollection principals, Permission permission) {
        AuthorizationInfo info = getAuthorizationInfo(principals);
        return isPermitted(permission, info);
    }
  1. isPermitted(Permission permission, AuthorizationInfo info)

implies 为 Shrio 校验权限的核心

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

Permission 接口👇

public interface Permission {
    boolean implies(Permission p);
}

WildcardPermission 实现了 Permission 接口为 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) {
            // 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;
    }

还是看这个吧,我自己琢磨写完👆,然后看了这个👇发现自己写的像 shit 😓

https://www.iteye.com/blog/jinnianshilongnian-2020017

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值