数据库中相关的表
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
- 获取当前用户名称
String userName = (String) principals.getPrimaryPrincipal();
- 创建 SimpleAuthorizationInfo(Simple POJO implementation of the AuthorizationInfo interface that stores roles and permissions as internal attributes.)
SimpleAuthorizationInfo auth = new SimpleAuthorizationInfo();
- 查询角色表和权限表中对应数据,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 方法:
- 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.");
}
}
}
- isPermitted(PrincipalCollection principals, Permission permission)
// 获取授权时存的权限信息
public boolean isPermitted(PrincipalCollection principals, Permission permission) {
AuthorizationInfo info = getAuthorizationInfo(principals);
return isPermitted(permission, info);
}
- 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 😓