Apache Shiro 授权

授权,也称为“访问控制”,是 Management 对资源访问的过程。换句话说,控制可以访问应用程序中的什么

授权检查的示例包括:是否允许用户查看此网页,编辑此数据,查看此按钮或打印到该打印机?这些都是决定用户有权访问的内容的决定。

授权要素

授权具有三个在 Shiro 中引用的核心元素:权限,角色和用户。

Permissions

Apache Shiro 中的权限代表了安全策略中最基本的元素。从根本上讲,它们是有关行为的声明,并明确表示可以在应用程序中完成的操作。格式正确的权限声明本质上描述了资源以及Subject与这些资源进行交互时可以采取的措施。

权限声明的一些示例:

  • 开启 Files

  • 查看“ /用户/列表”网页

  • Print documents

  • 删除“ jsmith”用户

大多数资源将支持典型的 CRUD(创建,读取,更新,删除)操作,但是对特定资源类型有意义的任何操作都是可以的。基本思想是,权限声明至少基于* Resources Actions *。

在查看权限时,可能要意识到的最重要的事情是,权限语句不表示谁可以执行所表示的行为。它们仅是*在应用程序中可以做什么的 Statements。

Permissions represent behavior only

权限语句仅反映行为(与资源类型相关的动作)。它们不反映能够执行这种行为。

定义允许谁(用户)执行什么(权限)是一种以某种方式向用户分配权限的练习。这始终由应用程序的数据模型完成,并且在不同应用程序之间可能会有很大差异。

例如,权限可以分组在一个角色中,并且该角色可以与一个或多个用户对象相关联。或者某些应用程序可以具有一个用户组,并且可以为一个组分配一个角色,这通过传递关联将意味着该组中的所有用户都隐式地获得了该角色中的权限。

如何授予用户权限有许多变体-应用程序根据应用程序需求确定如何对此建模。

我们将介绍 Shiro 如何确定Subject是否允许以后做某事。

Permission Granularity

首先,权限示例指定对资源类型(门,文件,Client 等)的操作(打开,读取,删除等)。在某些情况下,他们甚至指定非常精细的* instance-level *行为-例如,使用用户名'jsmith'(实例标识符)'删除'(action)'用户'(资源类型)。在 Shiro 中,您可以精确定义这些语句的粒度。

我们将在 Shiro 的Permissions Documentation中更详细地介绍权限粒度和权限声明的“级别”。

Roles

角色是一个命名实体,通常代表一组行为或职责。这些行为转化为您可以使用软件应用程序执行或无法执行的操作。通常将角色分配给用户帐户,因此通过关联,用户可以“执行”归因于各种角色的事情。

实际上有两种类型的角色,并且 Shiro 支持这两种概念:

  • 隐式角色 :大多数人将角色用作“隐式”构造:您的应用程序仅隐含基于角色名称的一组行为(即权限)。在具有隐式角色的情况下,在软件级别上没有任何内容说“允许角色 X 执行行为 A,B 和 C”。行为仅由名称暗示。

Potentially Brittle Security

虽然更简单,最常见的方法是隐式角色,但可能会带来很多软件维护和 Management 问题。

例如,如果您只想添加或删除角色,或稍后重新定义角色的行为,该怎么办?每当需要进行这样的更改时,您都必须返回源代码并更改所有角色检查,以反映安全模型中的更改!更不用说会产生的运营成本(重新测试,通过质量检查,关闭应用程序,使用新角色检查升级软件,重新启动应用程序等)。

对于非常简单的应用程序(例如,可能有“Management 员”角色和“其他所有人”),这可能是可以的。但是对于更复杂或可配置的应用程序,这可能是整个应用程序生命周期内的主要重大问题,并为您的软件带来巨大的维护成本。

  • 显式角色 :但是,显式角色本质上是实际权限声明的命名集合。以这种形式,应用程序(和 Shiro)确切地知道具有或没有特定角色的含义。因为已知可以执行或不能执行的“精确”行为,所以没有猜测或暗示特定角色可以执行或不能执行的操作。

Shiro 团队提倡使用权限和显式角色,而不是较早的隐式方法。您将对应用程序的安全体验有更大的控制权。

Resource-Based Access Control

一定要阅读 Les Hazlewood 的文章新的 RBAC:基于资源的访问控制,该文章深入介绍了使用权限和显式角色(以及它们对源代码的积极影响)而不是旧的隐式角色方法的好处。

Users

用户本质上是应用程序的“谁”。但是,正如我们之前介绍的那样,Subject实际上是 Shiro 的“用户”概念。

允许用户(主题)通过与角色或直接权限的关联在您的应用程序中执行某些操作。您的应用程序的数据模型精确定义了允许Subject做某事的方式。

例如,在您的数据模型中,也许您有一个实际的User类,并且直接向User实例分配权限。或者,也许您只直接将权限分配给Roles,然后将角色分配给Users,所以通过关联,Users可传递地“拥有”分配给其角色的权限。或者您可以用“组”概念来表示这些东西。这取决于您-使用对您的应用程序有意义的内容。

您的数据模型准确定义了授权将如何起作用。 Shiro 依靠Realm实现将您的数据模型关联详细信息转换为 Shiro 理解的格式。我们将在稍后介绍 Realms 如何做到这一点。

Note

最终,您的Realm实现是与数据源(RDBMS,LDAP 等)进行通信的对象。因此,您的境界将告诉 Shiro 是否存在角色或权限。您可以完全控制授权模型的结构和定义方式。

Authorizing Subjects

在 Shiro 中执行授权可以通过 3 种方式完成:

  • 以编程方式-您可以使用ifelse块之类的结构在 Java 代码中执行授权检查。

  • JDKComments-您可以将授权 Comments 附加到 Java 方法

  • JSP/GSP TagLibs-您可以根据角色和权限控制 JSP 或 GSP 页面输出

Programmatic Authorization

执行授权的最简单,最常见的方法是直接以编程方式与当前Subject实例进行交互。

Role-Based Authorization

如果要基于更简单/传统的隐式角色名称控制访问,则可以执行角色检查:

Role Checks

如果您只想查看当前Subject是否具有角色,则可以在Subject实例上调用变体hasRole*方法。

例如,要查看Subject是否具有特定(单个)角色,可以调用subject. hasRole(roleName)方法,并做出相应的反应:

Subject currentUser = SecurityUtils.getSubject();

if (currentUser.hasRole("administrator")) {
    //show the admin button 
} else {
    //don't show the button?  Grey it out? 
}

您可以根据需要调用几种面向角色的Subject方法:

Subject MethodDescription
hasRole(String roleName)如果为Subject分配了指定角色,则返回true,否则返回false
hasRoles(List<String> roleNames)返回与方法参数中的索引相对应的hasRole个结果的数组。如果需要执行许多角色检查(例如,自定义复杂视图时),可作为性能增强的有用工具
hasAllRoles(Collection<String> roleNames)如果为Subject分配了所有指定角色,则返回true,否则返回false

Role Assertions

除了检查boolean以查看Subject是否具有角色之外,您还可以在执行逻辑之前简单地 assert 它们具有预期的角色。如果Subject没有预期的作用,则将抛出AuthorizationException。如果它们确实发挥了预期的作用,则声明将安静地执行,并且逻辑将按预期 continue。

For example:

Subject currentUser = SecurityUtils.getSubject();

//guarantee that the current user is a bank teller and 
//therefore allowed to open the account: 
currentUser.checkRole("bankTeller");
openBankAccount();

这种方法相对于hasRole*方法的好处是,代码可以更简洁一些,因为如果当前Subject不满足预期条件(如果您不希望这样做),则不必构造自己的AuthorizationExceptions

您可以根据需要调用几种面向角色的Subjectassert 方法:

Subject MethodDescription
checkRole(String roleName)如果为Subject分配了指定的角色,则安静地返回;否则,则返回AuthorizationException
checkRoles(Collection<String> roleNames)如果Subject被分配了指定角色的“全部”,则静默返回;否则,则返回AuthorizationException
checkRoles(String... roleNames)与上面的checkRoles方法效果相同,但允许使用 Java 5 var-args 样式参数。

Permission-Based Authorization

如上文在角色概述中所述,执行访问控制的更好方法通常是基于权限的授权。基于权限的授权,因为它与应用程序的原始功能(以及应用程序的核心资源上的行为)紧密相关,所以,基于权限的授权源代码在您的功能更改时(而不是在安全策略更改时)更改。这意味着与类似的基于角色的授权代码相比,代码受到的影响要小得多。

Permission Checks

如果要检查Subject是否被允许执行某项操作,则可以调用各种isPermitted*方法变体。有两种主要的权限检查方法-基于对象的Permission实例或表示Permissions的字符串

基于对象的权限检查

执行权限检查的一种可能方法是实例化 Shiro 的org.apache.shiro.authz.Permission接口的实例,并将其传递给接受权限实例的*isPermitted方法。

例如,请考虑以下情形:在办公室中有一个Printer的唯一标识符laserjet4400n。我们的软件需要检查是否允许当前用户在该打印机上打印文档,然后才允许他们按“打印”按钮。权限检查,看是否可以这样写:

Permission printPermission = new PrinterPermission("laserjet4400n", "print");

Subject currentUser = SecurityUtils.getSubject();

if (currentUser.isPermitted(printPermission)) {
    //show the Print button 
} else {
    //don't show the button?  Grey it out?
}

在这个示例中,我们还看到了一个非常强大的* instance-level 访问控制检查的示例-基于单个数据实例*限制行为的能力。

基于对象的Permissions在以下情况下很有用:

  • 您需要编译时类型安全

  • 您想保证权限被正确表示和使用

  • 您需要显式控制权限解析逻辑(基于 Permission 接口的implies方法,称为权限隐含逻辑)的执行方式。

  • 您想保证权限能够准确反映应用程序资源(例如,也许可以在项目的构建过程中基于项目的域模型自动生成权限类)。

您可以根据需要调用几种面向对象权限的Subject方法:

Subject MethodDescription
isPermitted(Permission p)如果Subject被允许执行操作或访问由指定Permission实例汇总的资源,则返回true,否则返回_4.
isPermitted(List<Permission> perms)返回与方法参数中的索引相对应的isPermitted个结果的数组。如果需要执行许多权限检查(例如,自定义复杂视图时),可以用作性能增强
isPermittedAll(Collection<Permission> perms)如果Subject被允许所有指定的权限,则返回true,否则返回false

基于字符串的权限检查

尽管基于对象的权限可能有用(编译时类型安全,保证的行为,自定义的蕴涵逻辑等),但对于许多应用程序来说,有时它们可能会有些“繁重”。一种替代方法是使用普通的Strings代表权限实例。

例如,基于上面的打印许可权示例,我们可以将其重新格式化为基于String的许可权检查:

Subject currentUser = SecurityUtils.getSubject();

if (currentUser.isPermitted("printer:print:laserjet4400n")) {
    //show the Print button
} else {
    //don't show the button?  Grey it out? 
}

此示例仍然显示相同的实例级权限检查,但是权限的重要部分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) {
    //show the Print button
} else {
    //don't show the button?  Grey it out?
}

Shiro 的Permission documentation详细介绍了WildcardPermission令牌的格式和形成方式。

尽管上述 String 默认为WildcardPermission格式,但您实际上可以发明自己的 String 格式,并根据需要使用它。我们将在下面的“领域授权”部分中介绍如何执行此操作。

基于字符串的权限很有用,因为您不必强制实现接口,而且简单的字符串通常易于阅读。缺点是您没有类型安全性,并且如果需要更复杂的行为(超出 Strings 表示的范围),则需要基于权限接口实现自己的权限对象。实际上,大多数 Shiro 最终用户为简单起见选择基于字符串的方法,但是最终您的应用程序的要求将决定哪种更好。

与基于对象的权限检查方法一样,有一些 String 变体来支持基于字符串的权限检查:

Subject MethodDescription
isPermitted(String perm)如果Subject被允许执行操作或访问由指定的String权限汇总的资源,则返回true,否则返回_4.
isPermitted(String... perms)返回与方法参数中的索引相对应的isPermitted个结果的数组。如果需要执行许多String权限检查(例如,自定义复杂视图时),则可作为性能增强的工具
isPermittedAll(String... perms)如果指定的String许可全部允许Subject,则返回true,否则返回false

Permission Assertions

作为检查boolean以查看Subject是否被允许做某事的替代方法,您可以简单地 assert 在执行逻辑之前它们具有预期的权限。如果不允许Subject,将抛出AuthorizationException。如果按预期允许它们,则 assert 将安静执行,逻辑将按预期 continue。

For example:

Subject currentUser = SecurityUtils.getSubject();

//guarantee that the current user is permitted 
//to open a bank account: 
Permission p = new AccountPermission("open");
currentUser.checkPermission(p);
openBankAccount();

或者,使用 String 权限进行相同的检查:

Subject currentUser = SecurityUtils.getSubject();

//guarantee that the current user is permitted 
//to open a bank account: 
currentUser.checkPermission("account:open");
openBankAccount();

这种方法相对于isPermitted*方法的好处是,代码可以更简洁一些,因为如果当前Subject不满足预期条件(如果您不希望这样做),则不必构造自己的AuthorizationExceptions

您可以根据需要调用几种面向权限的Subjectassert 方法:

Subject MethodDescription
checkPermission(Permission p)如果允许Subject执行操作或访问由指定Permission实例汇总的资源,则静默返回,否则返回AuthorizationException
checkPermission(String perm)如果允许Subject执行操作或访问由指定的String权限汇总的资源,则安静地返回,否则返回AuthorizationException
checkPermissions(Collection<Permission> perms)如果允许Subject 全部指定的权限,则安静地返回;否则,则返回AuthorizationException
checkPermissions(String... perms)与上面的checkPermissions方法相同,但使用的是基于String的权限。

Annotation-based Authorization

除了Subject API 调用之外,如果您更喜欢基于元的授权控制,Shiro 还提供了 Java 5 注解的集合。

Configuration

在使用 Java 注解之前,您需要在应用程序中启用 AOP 支持。有许多不同的 AOP 框架,因此,不幸的是,没有在应用程序中启用 AOP 的标准方法。

对于 AspectJ,您可以查看我们的AspectJ 示例应用程序

对于 Spring 应用程序,您可以查看我们的Spring Integration文档。

对于 Guice 应用程序,您可以查看我们的Guice Integration文档。

RequiresAuthentication 注解

RequiresAuthentication注解要求当前的Subject在其当前会话期间已经过身份验证,以便可以访问或调用带 Comments 的类/实例/方法。

For example:

@RequiresAuthentication
public void updateAccount(Account userAccount) {
    //this method will only be invoked by a
    //Subject that is guaranteed authenticated
    ...
}

这主要等效于以下基于主题的逻辑:

public void updateAccount(Account userAccount) {
    if (!SecurityUtils.getSubject().isAuthenticated()) {
        throw new AuthorizationException(...);
    }

    //Subject is guaranteed authenticated here
    ...
}

RequiresGuest 注解

RequiresGuest注解要求当前的 Subject 是“来宾”,也就是说,对于要访问或调用的带 Comments 的类/实例/方法,它们在上一会话中没有经过身份验证或记住。

For example:

@RequiresGuest
public void signUp(User newUser) {
    //this method will only be invoked by a
    //Subject that is unknown/anonymous
    ...
}

这主要等效于以下基于主题的逻辑:

public void signUp(User newUser) {
    Subject currentUser = SecurityUtils.getSubject();
    PrincipalCollection principals = currentUser.getPrincipals();
    if (principals != null && !principals.isEmpty()) {
        //known identity - not a guest:
        throw new AuthorizationException(...);
    }

    //Subject is guaranteed to be a 'guest' here
    ...
}

RequiresPermissions 注解

RequiresPermissions注解要求当前的主题被授予一个或多个权限,以便执行带注解的方法。

For example:

@RequiresPermissions("account:create")
public void createAccount(Account account) {
    //this method will only be invoked by a Subject
    //that is permitted to create an account
    ...
}

这主要等效于以下基于主题的逻辑:

public void createAccount(Account account) {
    Subject currentUser = SecurityUtils.getSubject();
    if (!subject.isPermitted("account:create")) {
        throw new AuthorizationException(...);
    }

    //Subject is guaranteed to be permitted here
    ...
}

RequiresRoles 权限

RequiresRolesComments 要求当前的 Subject 具有所有指定的角色。如果他们没有角色,则该方法将不会执行,并且会抛出 AuthorizationException。

For example:

@RequiresRoles("administrator")
public void deleteUser(User user) {
    //this method will only be invoked by an administrator
    ...
}

这主要等效于以下基于主题的逻辑:

public void deleteUser(User user) {
    Subject currentUser = SecurityUtils.getSubject();
    if (!subject.hasRole("administrator")) {
        throw new AuthorizationException(...);
    }

    //Subject is guaranteed to be an 'administrator' here
    ...
}

RequiresUser 注解

RequiresUser *Comments 要求当前的 Subject 为要访问或调用的带 Comments 的类/实例/方法的应用程序用户。 “应用程序用户”定义为具有已知身份的Subject,该身份由于在当前会话期间进行了身份验证而已知,或者从上一会话的“ RememberMe”服务中被记住。

@RequiresUser
public void updateAccount(Account account) {
    //this method will only be invoked by a 'user'
    //i.e. a Subject with a known identity
    ...
}

这主要等效于以下基于主题的逻辑:

public void updateAccount(Account account) {
    Subject currentUser = SecurityUtils.getSubject();
    PrincipalCollection principals = currentUser.getPrincipals();
    if (principals == null || principals.isEmpty()) {
        //no identity - they're anonymous, not allowed:
        throw new AuthorizationException(...);
    }

    //Subject is guaranteed to have a known identity here
    ...
}

JSP TagLib 授权

Shiro 提供了一个标记库,用于基于Subject状态控制 JSP/GSP 页面输出。 Web章节的JSP/GSP 标签库部分对此进行了介绍。

Authorization Sequence

现在,我们已经了解了如何基于当前的Subject执行授权,让我们看看在进行授权调用时 Shiro 内部会发生什么。

我们从Architecture章中获取了之前的 Schema,仅突出显示了与授权相关的组件。每个数字代表授权操作过程中的一个步骤:

步骤 1 :应用程序或框架代码调用Subject hasRole*checkRole*isPermitted*checkPermission*方法变体中的任何一种,并传入所需的任何权限或角色表示。

步骤 2Subject实例(通常是DelegatingSubject(或子类))通过调用securityManager几乎相同的hasRole*checkRole*isPermitted*checkPermission*方法变体来委派应用程序的SecurityManager(securityManager实现org.apache.shiro.authz.Authorizer接口,它定义了所有特定于主题的授权方法)。

步骤 3SecurityManager是基本的“伞”组件,通过调用authorizer各自的hasRole*checkRole*isPermitted*checkPermission*方法来中继/代理到其内部org.apache.shiro.authz.Authorizer实例。 authorizer实例默认为ModularRealmAuthorizer实例,该实例支持在任何授权操作期间协调一个或多个Realm实例。

步骤 4 :检查每个已配置的Realm,以查看其是否实现相同的Authorizer接口。如果是这样,则调用 Realm 各自的hasRole*checkRole*isPermitted*checkPermission*方法。

ModularRealmAuthorizer

如前所述,Shiro SecurityManager实现默认使用ModularRealmAuthorizer实例。 ModularRealmAuthorizer同样支持具有单个领域以及具有多个领域的应用程序。

对于任何授权操作,ModularRealmAuthorizer都会对其内部Realms的集合进行迭代,并按迭代 Sequences 与每个交互。每种Realm交互功能如下:

  • 如果Realm本身实现Authorizer接口,则调用其各自的Authorizer方法(hasRole*checkRole*isPermitted*checkPermission*)。

  • 如果 Realm 的方法导致异常,则该异常将作为AuthorizationException传播到Subject调用方。这会缩短授权过程,并且不会为该授权操作查询任何剩余的领域。

    • 如果 Realm 的方法是返回布尔值的hasRole*isPermitted*变量,并且返回值为true,则true值将立即返回,并且所有剩余的 Realms 都将短路。此行为作为一种性能增强而存在,通常,如果一个领域允许,则暗示允许主题。这有利于安全策略,在默认情况下,所有内容均被禁止,并且明确允许所有内容,这是最安全的安全策略类型。
  • 如果领域未实现Authorizer接口,则将其忽略。

领域授权命令

需要指出的是,与认证完全一样,ModularRealmAuthorizer将以* iteration *的 Sequences 与 Realm 实例进行交互。

ModularRealmAuthorizer有权访问SecurityManager上配置的Realm实例。执行授权操作时,它将遍历该集合,并且对于实现Authorizer接口本身的每个Realm,调用领域各自的Authorizer方法(例如hasRole*checkRole*isPermitted*checkPermission*)。

配置全局 PermissionResolver

在执行基于String的权限检查时,大多数 Shiro 的默认Realm实现都会先执行此字符串,然后再执行权限蕴含逻辑,将其转换为实际的Permission实例。

这是因为权限是根据隐含逻辑而不是直接进行相等性检查来评估的(有关隐含性与相等性的更多信息,请参见Permission文档)。隐含逻辑比通过字符串比较更好地用代码表示。因此,大多数领域需要将提交的权限字符串转换或“解析”为相应的代表性Permission实例。

为了帮助进行此转换,Shiro 支持了PermissionResolver的概念。大多数Shiro Realm 实现使用PermissionResolver支持其对Authorizer接口基于String的权限方法的实现:在 Realm 上调用这些方法之一时,它将使用PermissionResolver将字符串转换为 Permission 实例,并执行检查那样。

所有 Shiro Realm的实现默认为内部WildcardPermissionResolver,该内部WildcardPermissionResolver假定 Shiro 的WildcardPermission String 格式。

如果要创建自己的PermissionResolver实现(也许是为了支持自己的 Permission 字符串语法),并且希望所有已配置的Realm实例都支持该语法,则可以为所有可以配置一个的Realms全局设置PermissionResolver

例如,在shiro.ini中:

shiro.ini

globalPermissionResolver = com.foo.bar.authz.MyPermissionResolver
...
securityManager.authorizer.permissionResolver = $globalPermissionResolver
...

PermissionResolverAware

如果要配置全局PermissionResolver,则每个Realm都必须接收已配置的PermissionResolver ***必须实现PermisionResolverAware接口。这保证了可以将配置的实例中继到每个支持这种配置的Realm

如果您不想使用全局PermissionResolver或不想被PermissionResolverAware接口打扰,则始终可以使用PermissionResolver实例显式配置领域(假定存在与 JavaBeans 兼容的 setPermissionResolver 方法):

permissionResolver = com.foo.bar.authz.MyPermissionResolver

realm = com.foo.bar.realm.MyCustomRealm
realm.permissionResolver = $permissionResolver
...

配置全局 RolePermissionResolver

在概念上与PermissionResolver类似,RolePermissionResolver能够表示Realm进行权限检查所需的Permission实例。

但是,与RolePermissionResolver的主要区别在于 ImportingString角色名称,而不是权限字符串。

当需要将角色名称转换为Permission实例的具体集合时,RolePermissionResolver可以由Realm在内部使用。

这对于支持可能没有权限概念的旧数据源或不灵活数据源特别有用。

例如,许多 LDAP 目录存储角色名称(或组名称),但不支持将角色名称与具体权限相关联,因为它们没有“权限”概念。基于 Shiro 的应用程序可以使用存储在 LDAP 中的角色名称,但可以实现RolePermissionResolver将 LDAP 名称转换为一组显式权限,以执行首选的显式访问控制。权限关联将存储在另一个数据存储中,可能是本地数据库。

因为将角色名称转换为权限的概念是特定于应用程序的,所以 Shiro 的默认Realm实现不使用它们。

但是,如果要创建自己的RolePermissionResolver并具有多个要配置的Realm实现,则可以为所有可以配置一个的Realms全局设置RolePermissionResolver

shiro.ini

globalRolePermissionResolver = com.foo.bar.authz.MyPermissionResolver
...
securityManager.authorizer.rolePermissionResolver = $globalRolePermissionResolver
...

RolePermissionResolverAware

如果要配置全局RolePermissionResolver,则每个Realm都必须接收已配置的RolePermissionResolver ***必须实现RolePermisionResolverAware接口。这样可以确保已配置的全局RolePermissionResolver实例可以中继到每个支持这种配置的Realm

如果您不想使用全局RolePermissionResolver或不想被RolePermissionResolverAware接口打扰,则始终可以使用RolePermissionResolver实例显式配置一个领域(假定存在与 JavaBeans 兼容的 setRolePermissionResolver 方法):

rolePermissionResolver = com.foo.bar.authz.MyRolePermissionResolver

realm = com.foo.bar.realm.MyCustomRealm
realm.rolePermissionResolver = $rolePermissionResolver
...

Custom Authorizer

如果您的应用程序使用多个领域来执行授权,并且ModularRealmAuthorizer的默认基于简单迭代的短路授权行为不符合您的需求,则您可能需要创建自定义Authorizer并相应地配置SecurityManager

例如,在shiro.ini中:

[main]
...
authorizer = com.foo.bar.authz.CustomAuthorizer

securityManager.authorizer = $authorizer