相关阅读
- Spring Shiro流程简析 基于过滤器的权限管理
- Spring Shiro流程简析 注解驱动的权限管理
- Spring Shiro基础组件 PermissionResolver
- Spring Shiro基础组件 Realm
简介
权限表示执行操作或者访问资源的能力,是系统安全策略中最细粒度或原子性的单元,是构建细粒度安全模型的基石;
核心方法
/**
* 是否包含指定的权限
*/
boolean implies(Permission p);
实现子类
public interface Permission
public class AllPermission implements Permission, Serializable
public class WildcardPermission implements Permission, Serializable
public class DomainPermission extends WildcardPermission
AllPermission
简介
全权限,包含所有的权限;
核心方法
/**
* 是否包含指定的权限
*/
public boolean implies(Permission p) {
// 总是返回true,表示拥有所有的权限
return true;
}
WildcardPermission
简介
实现了一种非常灵活的权限结构,支持多级权限匹配;
格式:domain:action:instance,各部分可多值,可使用通配符"*”,",”分隔;
例如:group,user:create,delete:*表示对任意的group/user的实例拥有创建/删除的权限;
核心方法
// 通配符
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;
/**
* 构造函数
*/
public WildcardPermission(String wildcardString) {
// 默认大小写不敏感
this(wildcardString, DEFAULT_CASE_SENSITIVE);
}
/**
* 设置权限结构
*/
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.");
}
// 大小写不敏感则统一小写化处理
if (!caseSensitive) {
wildcardString = wildcardString.toLowerCase();
}
// 分隔各部分内容
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 (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.");
}
}
/**
* 是否包含指定权限
*/
public boolean implies(Permission p) {
// 只支持和WildcardPermission比较
// 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;
}
DomainPermission
简介
提供方便类型安全/特定领域的子类可以扩展的基础类;
将权限部分分为:domain、actions、targets;
核心方法
// 领域
private String domain;
// 动作集合
private Set<String> actions;
// 实例集合
private Set<String> targets;
/**
* 构造函数
*/
public DomainPermission(String actions, String targets) {
// 根据类名得到领域名称
this.domain = getDomain(getClass());
this.actions = StringUtils.splitToSet(actions, SUBPART_DIVIDER_TOKEN);
this.targets = StringUtils.splitToSet(targets, SUBPART_DIVIDER_TOKEN);
encodeParts(this.domain, actions, targets);
}
/**
* 根据类名得到领域名称
*/
protected String getDomain(Class<? extends DomainPermission> clazz) {
String domain = clazz.getSimpleName().toLowerCase();
//strip any trailing 'permission' text from the name (as all subclasses should have been named):
int index = domain.lastIndexOf("permission");
if (index != -1) {
domain = domain.substring(0, index);
}
return domain;
}
/**
* 设置权限结构
*/
protected void setParts(String domain, Set<String> actions, Set<String> targets) {
String actionsString = StringUtils.toDelimitedString(actions, SUBPART_DIVIDER_TOKEN);
String targetsString = StringUtils.toDelimitedString(targets, SUBPART_DIVIDER_TOKEN);
encodeParts(domain, actionsString, targetsString);
this.domain = domain;
this.actions = actions;
this.targets = targets;
}
/**
* 设置权限结构
*/
private void encodeParts(String domain, String actions, String targets) {
if (!StringUtils.hasText(domain)) {
throw new IllegalArgumentException("domain argument cannot be null or empty.");
}
StringBuilder sb = new StringBuilder(domain);
if (!StringUtils.hasText(actions)) {
if (StringUtils.hasText(targets)) {
// 无动作有实例时,则动作描述为通配符
// 既无动作也无实例时,则只存在领域
sb.append(PART_DIVIDER_TOKEN).append(WILDCARD_TOKEN);
}
} else {
// 有动作则拼接动作信息
sb.append(PART_DIVIDER_TOKEN).append(actions);
}
if (StringUtils.hasText(targets)) {
// 有实例则拼接实例信息
sb.append(PART_DIVIDER_TOKEN).append(targets);
}
// 调用父类方法设置权限结构
setParts(sb.toString());
}