Permissions 权限
Shiro定义了一个许可声明, 定义了一个明确的行为或行动. 这是一个原始功能的声明在一个应用程序而已. 权限是最低级别的构造安全策略, 他们只明确定义应用程序可以做什么.
他们不描述谁能够执行的操作.
一些权限的例子:
- 打开一个文件
- 浏览 '/user/list' 网页
- 打印文件
- 删除用户 'jsmith'
规定谁(用户)允许做什么(权限)在某种程度上是分配权限的一种习惯做法. 这始终是通过应用程序数据模型来完成的, 并且在不同应用程序之间差异很大.
例如, 权限可以组合到一个角色中, 且该角色能够关联一个或多个用户对象. 或者某些应用程序能够拥有一组用户, 且这个组可以被分配一个角色, 通过传递的关联, 意味着所有在该组的用户隐式地获得了该角色的权限.
如何授予用户权限可以有很多变化 —— 基于应用需求来决定如何使其模型化.
通配符的权限
上述权限的例子, "打开文件"、"浏览'/user/list' 网页", 等都是有效的权限. 然而, 计算并解释这些自然语言字符串和确定用户是否允许执行这一行为将是非常困难的过程.
为了使此过程更容易处理且仍可读权限语句, Shiro 提供了强大的和直观的语法我们称之为 WildcardPermission
Simple Usage 简单示例
你想保护访问公司的打印机, 这样有些人就可以打印到特定的打印机, 而其他人可以查询什么工作目前在队列中.
一个非常简单的方法是授予用户 "queryPrinter" 权限. 然后你可以通过调用检查用户是否有 "queryPrinter" 权限:
subject.isPermitted("queryPrinter")
这是(大部分)相当于:
subject.isPermitted( new WildcardPermission("queryPrinter") )
但远不只这些.
简单的权限字符串可能在简单的应用程序中工作的很好, 但它需要你拥有像"printPrinter", "queryPrinter", "managePrinter"等权限. 你还可以通过使用通配符 "*" 授予用户任意权限(权限名任意), 这意味着他们在整个应用程序中拥有了所有的权限.
但使用这种方法没有办法说明用户具有 "所有打印机权限". 出于这个原因, Wildcard Permissions
(通配符权限) 支持多层次的权限管理.
多个部分
通配符权限支持多层次或部件(parts)的概念. 例如, 你可以通过授予用户权限来调整之前那个简单的例子
printer:query
在这个例子中的冒号是一个特殊字符, 它用来分隔权限字符串的下一部件.
在该例中, 第一部分是权限被操作的领域(printer), 第二部分是被执行的操作(query). 上面的例子将被改为:
printer:print
printer:manage
对于能够使用的部件是没有数量限制的, 因此它取决于你的想象, 依据你可能在你的应用程序中使用的方法
多值
每个部件能够保护多个值. 因此, 除了授予用户 "printer:print" 和 "printer:query" 权限外, 你可以简单地授予他们一个:
printer:print,query
它能够赋予用户 print 和 query 打印机的能力. 由于他们被授予了这两个操作, 你可以通过调用下面的语句来判断用户是否有能力查询打印机:
subject.isPermitted("printer:query")
该语句将会返回true
所有值
如果你想在一个特定的部件给某一用户授予所有的值呢? 这将是比手动列出每个值更为方便的事情. 同样, 基于通配符的话, 我也可以做到这一点. 若打印机域有3 个可能的操作(query, print 和manage), 可以像下面这样:
printer:query,print,manage
简单点变成这样:
printer:*
然后, 任何对 "printer:XXX" 的权限检查都将返回 true , 以这种方式使用的通配符比明确地列出操作具有更好的尺度, 如果你不久为应用程序增加了一个新的操作, 你不需要更新使用通配符那部分的权限.
最后, 还可以在通配符权限字符串的任何部分中使用通配符标记. 例如, 如果要在所有域(不只是打印机)中授予用户"查看"操作, 可以授予:
*:view
这样任何对 "foo:view" 的权限检查都将返回true
实例级的访问控制
另一种常见的通配符权限用法是塑造实例级的访问控制列表. 在这种情况下, 你可以使用三个部件 —— 第一个是域, 第二个是操作, 第三个是被付诸实施的实例.
简单例子
printer:query:lp7200
printer:print:epsoncolor
第一个定义了查询拥有ID: lp7200 的打印机的行为. 第二条权限定义了打印到拥有 ID: epsoncolor 的打印机的行为.
如果你授予这些权限给用户, 那么他们能够在特定的实例上执行特定的行为. 然后你可以在代码中做一个检查:
if ( SecurityUtils.getSubject().isPermitted("printer:query:lp7200") {
// 返回ID: lp7200 的打印机的当前任务
}
这是体现权限的一个极为有效的方法. 但同样, 为所有的打印机定义多个实例ID 不能很好的扩展, 尤其是当新的打印机添加到系统的时候. 你可以使用通配符来代替:
printer:print:*
这个做到了扩展, 因为它同时涵盖了任何新的打印机. 你甚至可以运行访问所有打印机上的所有操作
printer:*:*
或在一台打印机上的所有操作:
printer:*:lp7200
或甚至特定的操作:
printer:query, print:lp7200
*
通配符和,
子部件分离器可用于权限的任何部分
缺省的部分
最后要注意的是权限分配: 缺省的部分意味着用户可以访问所有与之匹配的值, 换句话说
printer:print
等价于
printer:print:*
并且
printer
等价于
printer:*:*
然而,你只能从字符串的结尾处省略部件,因此这样的:
printer:lp7200
并不等价于
printer:*:lp7200
检查权限
虽然权限分配使用通配符较为方便且具有扩展性 ("printer:print:*" = 允许在任意打印机上打印), 但在运行时的权限检查应该始终基于大多数具体的权限字符串.定义权限时可以模糊匹配, 但权限检查时必须确定具体权限
例如,如果用户有一个用户界面, 他们想打印一份文档到 lp7200 打印机, 你应该通过执行这段代码来检查用户是否被允许这样做:
if ( SecurityUtils.getSubject().isPermitted("printer:print:lp7200") ) {
//用 lp7200 打印机来打印文档
}
这种检查非常具体和明确地反映了用户在那一时刻试图做的事情. 然而, 下面这个运行是检查是不理想的:
if ( SecurityUtils.getSubject().isPermitted("printer:print") ) {
// 打印文档
}
为什么? 因为第二个例子表明对于下面的代码块的执行, 你必须能够打印到任何打印机. 请记住 "pinter:print" 是等价 "priner:print:*" 的 !
因此, 这是一个不正确的检查. 如果当前用户不具备打印到任何打印机的能力, 仅仅只有打印到 lp7200 和 epsoncolor 的能力, 该怎么办呢? 那么上面的第二个例子也绝不允许他们打印到 lp7200 打印机, 即使他们已被赋予了相应的能力 !
因此, 经验告诉我们在执行权限检查时, 应尽可能使用详细的权限字符串. 当然, 上面的第二块可能是在应用程序中别处的一个有效检查, 如果你真的想要执行该代码块, 比如用户被允许打印到任何打印机(令人怀疑, 但有可能). 你的应用程序将决定检查哪些有意义, 但一般情况下, 越具体越好.
隐含, 不相等(潜规则)
为什么运行时权限检查应该尽可能的具体, 但权限分配可以较为普通? 这是因为权限检查是通过隐含的逻辑来判断的 —— 而不是通过相等来检查.
也就是说, 如果一个用户被分配了 user: * 权限, 这意味着该用户可以执行 user:view 操作. user:* 字符串明显不等于 user:view , 但前者默认包含了后者. 因此 user:* 描述了 user:view 所定义的功能的一个超集
为了支持隐含规则, 所有权限都转换为实现 org.apache.shiro.authz.Permission
接口的对象实例. 这使得隐含逻辑可以在运行时执行, 并且隐含逻辑通常比简单的字符串相等性检查更复杂. 本文档中描述的所有通配符行为实际上都是由 org.apache.shiro.authz.permission.wildcardpmission
类实现的. 下面是一些更多的通配符权限字符串, 它们通过隐含方式确定如何访问:
user:*
同时隐含着删除一个用户的能力:
user:delete
同样地,
user:*:12345
同时隐含着更新 ID 为12345 的用户帐户的能力:
user:update:12345
而且
printer
隐含着打印到任何打印机的能力
printer:print
___________ .___ _____ _________ .__ __
\_ _____/ ____ __| _/ _____/ ____\ \_ ___ \| |__ _____ _______/ |_ ___________
| __)_ / \ / __ | / _ \ __\ / \ \/| | \\__ \ \____ \ __\/ __ \_ __ \
| \ | \/ /_/ | ( <_> ) | \ \___| Y \/ __ \| |_> > | \ ___/| | \/
/_______ /___| /\____ | \____/|__| \______ /___| (____ / __/|__| \___ >__|
\/ \/ \/ \/ \/ \/|__| \/
End of Chapter