授权,也被称为 访问 控制,它是管理资源访问的过程.换言之,也就是控制在一个程序中 谁 有权利访问 什么 .
授权检测的例子有: 此用户是否被允许查看此页面,编辑里面的数据,看到里面的按钮,或者从这台打印机打印?这些决定形成了一个用户可以访问什么的规则.
组成授权的元素
授权有三个核心元素,在 Shiro 中我们经常要用到它们: permissions(权限), roles(角色)和users(用户).
permissions(权限)
权限是 Shiro 中安全策略最基本的元素,它们是一组关于行为的基本指令,以明确表示在一个程序中什么可以做.一个很好定义的权限指令必须描述资源以及当一个 Subject
与这些资源交互时什么动作可以执行.
下面是一些权限指令的例子:
- 打开一个文件
- 查看'/user/list'页面
- 打印文档
- 删除'jsmith'用户
大部分资源都支持基本的 CRUD(create,read,update,delete)操作,排除某些特定资源类型,其他的任何操作都是可以的.权限设置最基础的思想是在 资源 和 操作 的基础上设置最小量的权限指令.
当看到各种权限时,最重要的一点是要是认识到一个权限指令不是描述谁可以执行这个动作,而是描述在应用中什么可以做.
权限只描述行为
权限指令只描述行为(和资源类型相关的动作), 并不关心谁有能力执行这个行为
定义谁(用户)被允许做什么(权限)需要用一些方法将权限赋给用户,这通常取决于程序的数据模型而且经常在程序中发生改变.
例如,一组权限可以归于一个角色而角色与一个或多个用户对象关联.或者一些程序可以有一组用户而一个组可以指定一个角色,在这里关系将被传递也就是说组内用户隐含被赋予角色拥有的权限.
有很多方式可以将权限赋予用户因为程序可以根据需求决定如何设计.
我们稍后讨论 Shiro 如何判断一个 Subject
是否被允许做某事.
权限粒度
上面的权限示例都是针对资源(入口,文件,客户等)执行特定的动作(打开,读取,删除等),在一些场景中,他们也会指定非常细粒度的 实例级别 行为-例如,'删除'(动作)名为'Jsmith'(实例)的'用户'(资源类型).在 Shiro 中,你可以精确定义指令到细致入微的程度.
我们在 Shiro 的 Permissions 文档中详细讨论权限粒度和权限指令的'级别'.
角色
角色是一个实体名,代表一组行为或职责.这些行为在程序中转换为你可以做或者不能做的事情.角色通常赋给用户帐号,关联后,用户就可以 做 属于不同角色的事情.
有两种有效的角色指定方式, Shiro 都支持:
- 隐式角色: 大部分用户将权限 隐含 于创建的角色中: 程序只是在一个角色名称上 隐含 了一组行为(也就是权限),使用时,在软件级别不会说"某角色X被允许执行行为A、B和C", 而是将行为隐含于一个单独的角色名字中.
潜在安全隐患
虽然这是一个非常简单和常用的方法, 但隐含的角色可能会增加软件的维护成本和管理问题.
例如, 如果你想增加或删除一个角色, 或者重定义角色的行为怎么办? 你不得不重新打开代码, 修改所有对更改后的角色的检测, 每次都需要这样做, 这还没提到其引起的执行代价(重测试, 通过质量验证, 关闭程序, 升级软件, 重启程序等).
对于简单程序这种方法可能适用(比如只有一个 'admin' 角色和 'everyone else' 角色), 但复杂的程序中, 这会成为你程序生命周期中一个主要的问题, 会给你的软件维护带来很大的代价.
- 显式指定: 显式的为角色指定权限, 角色本质上是一组权限指令的名称集,在这种形式下, 程序(以及 Shiro) 准确 知道一个特定的角色是什么意思, 因为它确切知道某行为是否可以执行, 而不用去猜测特定的角色可以或不可以做什么.
Shiro 团队提倡使用权限和显式为角色指定权限以替代原始的将权限隐含于角色中的方法, 这样你可以对程序安全提供更强的控制.
基于资源的访问控制
读一下Les Hazlewood的文章: The New RBAC: Resource-Based Access Control. 这篇文章深入讨论了使用权限和显式为角色指定权限代替旧的将权限隐含于角色中方法的好处(以及对源代码的影响).
链接地址: The New RBAC: Resource-Based Access Control
用户
一个用户本质上是程序中的'调用者', 如同我们前面提到的, Subject
实际上是 shiro 的 '用户'.
用户(Subjects)通过与角色或权限关联确定是否被允许执行程序内特定的动作, 你的程序数据模型确切定义了 Subject
是否允许做什么事情.
例如, 在你的数据模型中, 你定义了一个普通的用户
类并且直接为其用户
实例设置了权限, 或者你只是直接给角色
设置了权限, 然后将用户
与该角色关联. 通过这种关联, 用户
就等价与拥有了该角色所具备的权限, 或者你也可以通过'组'的概念来完成这件事, 这取决于你程序的设计.
你的数据模型定义了如何进行授权, Shiro 依赖一个 Realm
实现将你的数据模型关联转换成 Shiro 可以理解的格式, 我们将稍后讨论 Realms.
注意
最终,是 Realm 与你的数据源 (RDBMS,LDAP等) 做交流,Realm 用来告知Shiro 角色或权限是否存在,你可以完全控制你的授权模型如何创建和定义。
用户授权
在shiro中执行授权的操作可以有如下三种方法:
- 编程式 - 你可以在java代码里面通过结构化的
IF
和ELSE
代码块执行授权检查 - JDK注解式 - 你可以使用java注解附加到你的java方法上
- JSP/GSP标签库 - 你可以通过角色和权限来控制JSP或者GSP页面的输出显示
编程式授权
或许最简单和常用的执行权鉴的方式就是直接通过编程和当前用户Subject
对象实例进行交互.
基于角色的授权
如果你想基于简单或者传统的角色名来控制访问者,你可以像下面这样进行角色检查:
角色检查
如果你想简单地检查一下当前用户 Subject
是否拥有一个角色, 你可以在一个用户实例上调用 hasRole*
方法.
例如, 查看一个用户 Subject
是否有特定(单独)的角色, 你可以调用subject.hasRole(roleName)
方法, 并做出相应的反馈:
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.hasRole("administrator")) {
// 展示管理员按钮
} else {
// 不显示按钮? 按钮禁用并显示灰色?
}
这里有几个面向角色的用户
Subject
方法你可以使用,你可以根据需要随意使用:
用户Subject 方法 | 描述 |
---|---|
hasRole(String roleName) |
返回 true 如果用户 Subject 和指定角色关联, false 其他情况. |
hasRoles(List<String> roleNames) |
返回一个hasRole 数组结果,并且数组内的每一个检查结果都和传进来的角色参数向对应.如果需要执行许多角色检查, 则作为性能调优非常有用 (比如:当想要配置一个复杂的视图) |
hasAllRoles(Collection<String> roleNames) |
返回 true 如果 Subject 已经关联所有 指定角色, false 其他情况. |
角色判断
除了检查
布尔值
还有另一个方法检测用户 Subjet
是否是指定为某个角色, 你可以在的代码执行之前简单判断他们是否是所要求的角色, 如果用户 Subject
不是所要求的角色, AuthorizationException
(授权异常)对象将被抛出, 如果符合所要求的角色, 判断将安静地执行并按期望的顺序执行下面的逻辑.
例如:
Subject currentUser = SecurityUtils.getSubject();
// 保证当前用户是银行出纳员
// 因此允许开户:
currentUser.checkRole("bankTeller");
openBankAccount();
与 hasRole* 方法相比, 这种方法的好处在于代码更为清晰, 如果当前Subject 不满足所需条件你不需要建立你自己的
AuthorizationExceptions
异常(如果你不想那么做)
它们也有基于角色的方法, 你可以根据需要调用:
用户Subject 方法 | 描述 |
---|---|
checkRole(String roleName) |
如果用户Subject 拥有此角色程序将继续执行并且没有任何异常抛出,如果没有此角色程序将抛出一个AuthorizationException . |
checkRoles(Collection<String> roleNames) |
用户若关联所有角色, 程序就继续执行. 否则抛出AuthorizationException . |
checkRoles(String... roleNames) |
功能同上一个,不同的是这次参数是个不定长数组 |
基于权限的授权
就像我们上面在角色概述中提到的, 通过基于权限的授权执行访问控制是更好的方法. 基于权限的授权, 因为其与程序功能 (以及程序核心资源上的行为) 紧密联系, 基于权限授权的源代码在程序功能改变时才需要改变, 而与安全策略无关. 这意味着与同样基于角色的授权相比, 对代码的影响更少.
权限检查
如果你希望检查一个用户 Subject
是否被允许做某件事情, 你可以调用 isPermitted*
方法的变形, 有两种主要方式检查授权--基于对象的权限
实例和基于字符串的权限
表示.
基于对象的权限检查
执行权限检查的一种方法是实例化一个Shiro的org.apache.shiro.authz.Permission
接口并且将它传递给接收权限实例的 *isPermitted
方法.
例如, 假设一下以下情景: 办公室里有一台唯一标识为 laserjet4400n
的打印机
, 在我们向用户显示打印按钮之前, 软件需要检查当前用户是否被允许用这台打印机打印文档, 检查权限的方式可以会是这样:
Permission printPermission = new PrinterPermission("laserjet4400n", "print");
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.isPermitted( printPermission )) {
// 显示打印按钮
} else {
// 不显示? 禁用变灰?
}
在这个例子中, 我们同样看到了一个非常强大的 实例别 的访问控制检查 - 在 单数据实例 上限制行为的能力
基于对象的权限
对下列情况非常有用:
- 希望编译时类型安全
- 希望确保正确地引用和使用权限
- 希望对权限判断逻辑 (又称作权限隐含逻辑, 是基于权限接口的 implies方法) 执行方式进行显示控制
- 希望确保权限正确地反映程序资源 (例如, 在一个项目的域模型上创建一个对象时, 权限类可能自动产生)
下面是一些基于权限的你可以根据需要调用的用户 Subject
方法:
Subject Method | Description |
---|---|
isPermitted(Permission p) |
返回 true 如果用户 Subject 被允许执行一个操作或者访问一个资源 ,操作或资源就是 Permission 实例, 其他情况返回false . |
isPermitted(List<Permission> perms) |
返回一个数组的 isPermitted 结果, 同时一一对应参数里面的权限实例, 如果需要执行许多权限检查, 则作为性能优化非常有用(比如,自定义一个复杂的视图) |
isPermittedAll(Collection<Permission> perms) |
返回 true 如果用户 Subject 被授予所有指定的权限, 其他情况则返回false . |
基于字符串的权限检查
虽然基于对象的权限检查很有用 (编译期类型安全, 对行为担保, 定制隐含逻辑等) , 但在许多程序里有时候感觉有点笨重, 另一种选择是用普通的字符串
来代表权限.
例如, 对于上面打印权限的例子, 我们可以使用基于字符串
的权限检查达到同样的结果:
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.isPermitted("printer:print:laserjet4400n")) {
// 显示打印按钮
} else {
// 不显示打印按钮? 按钮禁用显示灰色?
}
这个例子同样展示了实例级权限检查, 但重要的权限部分-
printer
(资源类型),print
(动作),和laserjet4400n
(实例id)都用字符串体现了出来.
上面的例子展示了一种以冒号分割的特殊形式的字符串, 定义于Shiro的 org.apache.shiro.authz.permission.WildcardPermission 类中, 它适合大多数用户.
上面的代码块基本上是下面这段代码的缩写:
Subject currentUser = SecurityUtils.getSubject();
Permission p = new WildcardPermission("printer:print:laserjet4400n");
if (currentUser.isPermitted(p) {
// 显示打印按钮
} else {
// 不显示打印按钮? 按钮禁用显示灰色?
}
WildcardPermission
令牌格式和构成选项将在 Shiro 的 权限文档中深入讨论
上面的字符串使用默认的 WildcardPermission
格式, 实际上你可以创造并使用你自己的字符串格式, 我们将在下面 Realm 授权章节讨论如何操做.
基于字符串的权限有利的一面在于你不需要实现一个接口而且简单的字符串也非常易读, 而不利的一面在于不保证类型安全, 而且当你需要定义超出字符串表现能力之外的更复杂的行为时, 你仍旧需要利用权限接口实现你自己的权限对象. 实际上, 大部分 Shiro 的终端用户因为其简单而选择基于字符串的方式, 但最终你的程序需求决定了哪一种方法会更好.
和基于对象的权限检查方法一样, 下面是基于字符串权限检查的方法:
Subject Method | Description |
---|---|
isPermitted(String perm) |
如果Subject 被允许执行字符串 表达的动作或资源访问权限, 返回true ,否则返回false
|
isPermitted(String... perms) |
按照参数顺序返回isPermitted 的结果数组, 当许多字符串 权限需要检查时非常有用( 比如定制一个复杂的视图时 |
isPermittedAll(String... perms) |
当Subject 具备 所有 字符串 定义的权限时返回true , 否则返回false
|
权限判断
另一种检查 Subject
是否被允许做某件事的方法是, 在逻辑执行之前简单判断它是否具备所需的权限, 如果不具备, AuthorizationException
异常将被抛出, 如果是具备的, 判断成功, 并按期望顺序执行程序接下来的逻辑.
例如:
Subject currentUser = SecurityUtils.getSubject();
// 确保当前用户拥有开启账户的权限:
Permission p = new AccountPermission("open");
currentUser.checkPermission(p);
openBankAccount();
下面使用字符串权限检查执行同样的过程:
Subject currentUser = SecurityUtils.getSubject();
// 确保当前用户拥有开启账户的权限:
currentUser.checkPermission("account:open");
openBankAccount();
与
isPermitted*
方法相比较, 这种方法的优势是代码更为清晰, 如果当前 Subject
不符合条件, 你不必创建你自己的 AuthorizationExceptions
异常(如果你不想那么做)
下面是你可以根据需要调用的方法:
Subject Method | Description |
---|---|
checkPermission(Permission p) |
符合权限对象的规则就继续执行否则抛出 AuthorizationException 异常 |
checkPermission(String perm) |
符合权限字符串规则就继续执行否则抛出 AuthorizationException 异常 |
checkPermissions(Collection<Permission> perms) |
符合所有指定的权限规则就继续执行否则抛出 AuthorizationException 异常 |
checkPermissions(String... perms) |
功能同上一个, 参数变为字符串数组 |
基于注解的授权
在其他Subject
对象API调用中, Shiro提供了一个支持java5+注解的集合, 如果你更加喜欢使用注解来控制授权.
配置
在你可以使用java注解前, 你将需要开启AOP(面向切面编程)这一功能.下面列出了不同的支持AOP的框架,不幸的是,这里面没有一个标准的方式来在应用中使用AOP.
- AspectJ
- Spring
- Guice
RequiresAuthentication
注解
RequiresAuthentication注解要求当前用户已经认证过, 且必须是在用户的当前session可用时期内, 满足上述范围才可以访问或者调用被当前注解加持过的类/实例/方法.
比如:
@RequiresAuthentication
public void updateAccount(Account userAccount) {
// 只有当前用户已认证才可以调用此方法
...
}
逻辑上等价于:
public void updateAccount(Account userAccount) {
if (!SecurityUtils.getSubject().isAuthenticated()) {
throw new AuthorizationException(...);
}
// 用户已认证
...
}
RequiresGuest
注解RequiresGuest注解则需要当前用户必须是游客的身份,那就是说,他们必须是当前没有认证或者之前没有认证的用户, 满足此条件的情况下在注解加持下的类/实例/方法才会运行用户访问或调用.
比如:
@RequiresGuest
public void signUp(User newUser) {
// 只有当前用户是未知用户或者匿名用户才可以访问
...
}
逻辑上等价于:
public void signUp(User newUser) {
Subject currentUser = SecurityUtils.getSubject();
PrincipalCollection principals = currentUser.getPrincipals();
if (principals != null && !principals.isEmpty()) {
// 只有你不是游客
throw new AuthorizationException(...);
}
// 用户无法识别
...
}
RequiresPermissions
注解RequiresPermissions注解需要当前用户拥有一个或多个指定的权限, 才可以执行被注解加持的方法.
比如:
@RequiresPermissions("account:create")
public void createAccount(Account account) {
// 拥有指定权限
...
}
逻辑上等价于:
public void createAccount(Account account) {
Subject currentUser = SecurityUtils.getSubject();
if (!subject.isPermitted("account:create")) {
throw new AuthorizationException(...);
}
// 用户拥有对应的权限
...
}
RequireRoles
注解RequireRoles注解需要当前用户拥有所有指定的角色.如果不满足条件就抛出一个授权异常.
比如:
@RequiresRoles("administrator")
public void deleteUser(User user) {
//管理员角色才可以调用此方法
...
}
逻辑上等价于:
public void deleteUser(User user) {
Subject currentUser = SecurityUtils.getSubject();
if (!subject.hasRole("administrator")) {
throw new AuthorizationException(...);
}
// 当前用户拥有管理员角色
...
}
RequireUser
注解RequireUser注解需要当前用户已认证, 可以是刚认证的用户,也可以是之前认证后调用记住我方法的用户,总之要在程序中可以被唯一识别.这种用户才可以调用被此注解加持的类/实例/方法.
@RequiresUser
public void updateAccount(Account account) {
// shiro可以识别此用户就可以进入此方法
...
}
等价于:
public void updateAccount(Account account) {
Subject currentUser = SecurityUtils.getSubject();
PrincipalCollection principals = currentUser.getPrincipals();
if (principals == null || principals.isEmpty()) {
// 无法识别的用户
throw new AuthorizationException(...);
}
// 当前用户已认证
...
}
授权内部过程
现在我们已经知道如何基于当前用户来执行授权, 但具体shiro内部的执行细节却并不清晰, 所以这里让我们看看当授权调用的过程中Shiro内部究竟发生了什么.
我们将之前在结构一章中的结构图那过来,并且只高亮显示和授权相关的组件.每个数字代表授权操作中的一个步骤:
Step 1:应用或框架代码调用任何的用户权鉴方法比如
hasRole()
, checkRole()
, isPermitted()
, 或者 checkPermission()
,并将要验证的权限或角色参数传递进去.Step 2:用户实例,通常是一个
DelegatingSubject
类的实例或其子类, DelegatingSubject
类是一个代理用户, 内部不提供具体实现过程,而是在内部获取全局唯一的SecurityManager
并再次调用SecurityManager
类里面和第一步相同的方法.(SecurityManager
实现了org.apache.shiro.authz.Authorizer接口,Authorizer
接口定义了所有的用户授权方法)Step 3:
SecurityManager
作为一个基本的"伞"组件, 通过调用 authorizer
的各自的 hasRole()
, checkRole()
, isPermitted()
, checkPermission()
方法接替/代表其内部 org.apache.shiro.authz.Authorizer 实例. authorizer
默认情况下是一个 ModularRealmAuthorizer
实例, ModularRealmAuthorizer
支持协调一个或多个 Realm
实例如何执行任何授权操作.Step 4:每一个配置好的
Realm
类都会被检查是否实现了Authorizer
接口,如果实现了,Realm 自己的hasRole()
, checkRole()
, isPermitted()
, 或者 checkPermission()
方法将被调用.
通用realm授权(ModularRealmAuthorizer)
之前说到Shiro的SecurityManager
默认实现是一个ModularRealmAuthorizer
实例,ModularRealmAuthorizer
同时支持用一个 Realm 的程序和用多个 Realm 的程序.
对于任何授权操作, ModularRealmAuthorizer 将在其内部的 Realm 集合中迭代, 按迭代顺序同每一个 Realm 交互, 与每一个 Realm 交互的方法如下:
- 如果Realm实现了 Authorizer 接口, 调用它各自的授权方法(
hasRole()
、checkRole()
、isPermitted()
或checkPermission()
)
1.1 如果 Realm 函数的结果是一个异常(exception), 且该异常衍生自 AuthorizationException异常, 就切断授权过程, 剩余的授权 Realm 将不会继续调用.
1.2 如果 Realm 的方法是一个hasRole()
或isPermitted()
, 并且返回真, 则真值立即被返回而且剩余的 Realm 被短路, 这种做法作为一种性能增强, 在一个 Realm 判断允许后, 隐含认为这个 Subject 被允许. 它支持最安全的安全策略: 默认情况下所有都被禁止, 仅显示指定允许的内容. - 如果 Realm 没有实现
Authorizer
接口, 将被忽略.
Realm 授权顺序
需要指出非常重要的一点, 就如同验证(authentication)一样, ModularRealmAuthorizer 按 迭代/遍历 (iteration)顺序与 Realm 交互.
ModularRealmAuthorizer
拥有配置在 SecurityManager
内的 Realm
实例们的入口, 当执行一个授权操作时, 它将在整个集合中进行遍历(iteration), 对于每一个实现 Authorizer
接口的 Realm, 调用Realm 各自的 Authorizer
方法(如 hasRole()
、 checkRole()
、 isPermitted()
或 checkPermission()
).
配置全局的 PermissionResolver
当执行一个基于字符串
的权限检查时, 大部分 Shiro 默认的 Realm
将会在执行权限等 隐含 逻辑之前首先把这个字符串转换成一个真实的权限实例.
这是因为权限被认为是基于隐含逻辑而不是相等检查( 查看 权限 章节了解更多隐含与相等的对比). 隐含逻辑用代码表示要比通过字符串对比好, 因此, 大部分 Realm需要转换一个提交的权限字符串为对应的权限
实例.
为了这个转换目的, Shiro 支持 PermissionResolver
, 大部分 Shiro Realm
使用 PermissionResolver
来支持它们对Authorizer
接口中基于字符串
权限方法的实现: 当这些方法在Realm上被调用时, 将使用PermissionResolver
将字符串转换为权限实例, 并执行检查.
所有的 Shiro Realm
默认使用内部的 WildcardPermissionResolver
, 它使用 Shiro 的WildcardPermission
字符串格式.
如果你想创建你自己的 PermissionResolver
实现, 比如说你想创建你自己的权限字符串语法, 希望所有配置的Realm实例都支持这个语法, 你可以把自己的 PermissionResolver 设置成全局, 供所有 realm 使用.
比如, 在shiro.ini中进行如下配置:
globalPermissionResolver = com.foo.bar.authz.MyPermissionResolver
...
securityManager.authorizer.permissionResolver = $globalPermissionResolver
...
PermissionResolverAware
如果你配置了全局的PermissionResolverAware
, 每个想要接受配置的Realm
都必须
实现PermisionResolverAware
接口. 这保证了配置过的实例可以传递到每一个支持配置的Realm.
如果你不想使用一个全局的 PermissionResolver
或者你不想被 PermissionResolverAware
接口麻烦, 你可以显式地为单个的 Realm 配置 PermissionResolver
接口实例( 可以看作是JavaBean的setPermissionResolver 方法):
permissionResolver = com.foo.bar.authz.MyPermissionResolver
realm = com.foo.bar.realm.MyCustomRealm
realm.permissionResolver = $permissionResolver
配置一个全局的RolePermissionResolver
与 PermissionResolver
类似, RolePermissionResolver
也有能力表示执行权限检查的 Realm 所需的权限实例.
最主要的不同在于RolePermissionResolver
接收的字符串是一个角色名, 而不是一个权限字符串.
RolePermissionResolver
在被 Realm
需要时用来转换一个角色名到一组显式的权限实例.
这是非常有用的, 它支持那些遗留的或者不灵活的没有权限概念的数据源.
例如, 许多 LDAP 目录存储角色名称(或组名)但不支持角色名和权限的联合, 因为它没有权限的概念. 一个使用 shiro 的程序可以使用存储于 LDAP 的角色名, 但需要实现一个 RolePermissionResolver
来转换 LDAP 名到一组确切的权限中以执行显式的访问控制, 权限的联合将被存储于其它的数据存储中, 比如说本地数据库.
因为这种将角色名转换为权限的概念是特定的, Shiro 默认的 Realm 没有使用它们.
然而, 如果你想创建你自己的 RolePermissionResolver
并且希望用它配置多个 Realm
实现, 你可以将你的 RolePermissionResolver
设置成全局.
shiro.ini
globalRolePermissionResolver = com.foo.bar.authz.MyPermissionResolver
securityManager.authorizer.rolePermissionResolver = $globalRolePermissionResolver
RolePermissionResolverAware
如果你想配置一个全局的 RolePermissionResolver
, 每个要使用此配置的 Realm
必须实现了 RolePermisionResolverAware
接口. 这保证了配置的全局 RolePermissionResolver
实例可以传递到各个支持这样配置的 Realm
.
如果你不想使用全局的 RolePermissionResolver
或者你不想麻烦的实现 RolePermissionResolverAware
接口, 你同样可以单独为一个 realm 配置 RolePermissionResolver
(可以看作 JavaBean 的 setRolePermissionResolver 方法).
rolePermissionResolver = com.foo.bar.authz.MyRolePermissionResolver
realm = com.foo.bar.realm.MyCustomRealm
realm.rolePermissionResolver = $rolePermissionResolver
定制Authorizer
如果你的程序使用多于一个 Realm 来执行授权而 ModularRealmAuthorizer
默认的简单遍历(iteration), 短路授权的方式不能满足你的需求, 你可以创建自己的 Authorizer
并配置相应的 SecurityManager
例如,在 shiro.ini
中:
[main]
authorizer = com.foo.bar.authz.CustomAuthorizer
securityManager.authorizer = $authorizer
___________ .___ _____ _________ .__ __
\_ _____/ ____ __| _/ _____/ ____\ \_ ___ \| |__ _____ _______/ |_ ___________
| __)_ / \ / __ | / _ \ __\ / \ \/| | \\__ \ \____ \ __\/ __ \_ __ \
| \ | \/ /_/ | ( <_> ) | \ \___| Y \/ __ \| |_> > | \ ___/| | \/
/_______ /___| /\____ | \____/|__| \______ /___| (____ / __/|__| \___ >__|
\/ \/ \/ \/ \/ \/|__| \/
End of Chapter