对于Role-Permission两级权限体系,大多数情况下,我们都是直接使用Shiro提供的WildcardPermission来实现细粒度的权限控制。
Shiro为我们定义了一个抽象的权限描述字串:
【资源】:【操作】:【实例】
例如:”user:create,update”,”user:delete:100110”,等等。
Shiro的通配符权限,可以这样使用【以下描述中『授权』指分配给当前用户的权限,而『校验』指在注解或JSP标签中写的权限】:
- 授权(user),校验(user),结果(通过)
- 授权(user),校验(user:create),结果(通过)
- 授权(user:create),校验(user:update),结果(不通过)
- 授权(user:create),校验(user:create,update),结果(不通过)
- 授权(user:create,update),校验(user:update),结果(通过)
- 授权(user:create),校验(user:*),结果(不通过)
- 授权(user:*),校验(user:create),结果(通过)
- 。。。
那么,这个支持通配符Permission的内部逻辑又是如何实现的,是一个比较有意思的话题。
来看代码:
public boolean implies(Permission p) {
// 只支持与另一个WildcardPermission作比较
if (!(p instanceof WildcardPermission)) {
return false;
}
WildcardPermission wp = (WildcardPermission) p;
List<Set<String>> otherParts = wp.getParts();
int i = 0;
for (Set<String> otherPart : otherParts) {
// 如果当前permission长度比other permission短,校验通过
// 参见示例(2)
if (getParts().size() - 1 < i) {
return true;
} else {
Set<String> part = getParts().get(i);
//如果当前permission的当前Part不是*,
//且又不能包住other permission的当前Part,则校验不通过
//否则继续,直到处理完当前permission的所有节
// 参见示例(3)(4)
if (!part.contains(WILDCARD_TOKEN) && !part.containsAll(otherPart)) {
return false;
}
i++;
}
}
// 上面逻辑没有返回false,开始检查当前permission多出来的part
// 如果多出来的part只要有一个不为*,则不通过。
// 参见示例(7)
for (; i < getParts().size(); i++) {
Set<String> part = getParts().get(i);
if (!part.contains(WILDCARD_TOKEN)) {
return false;
}
}
return true;
}
代码加了注释,从这个实现逻辑来看,尤其是那一句!part.containsAll(otherPart)
,我们就明白了缺省情况下,permission如果用逗号分隔多个时,是『与』的关系,而非『或』。
这时,我们来回忆一下在注解中,有这样的写法@RequiresPermissions(value={"channel:edit","channel:create"},logical=Logical.OR)
,说明Shiro还是在帮我们处理多个权限共存时,AND或OR的问题。所以,这里不能用@RequiresPermissions("channel:edit,create")
。
也就是说,我们常说的一个原则『分配权限时可以模糊,但校验时一定要明确』,参见示例7(正例),6(反例)。