我正在开发一个需要基于对象进行身份验证和授权的服务器端应用程序。我喜欢Shiro框架的简单性,但为了与JAAS兼容,我编写了一个使用Apache Shiro作为底层机制的LoginModule。
但我的问题是,我无法找到一种将JAAS授权检查委托给Shiro的方法。我该如何实现这一点?
我正在开发一个需要基于对象进行身份验证和授权的服务器端应用程序。我喜欢Shiro框架的简单性,但为了与JAAS兼容,我编写了一个使用Apache Shiro作为底层机制的LoginModule。
但我的问题是,我无法找到一种将JAAS授权检查委托给Shiro的方法。我该如何实现这一点?
从概念上看,你似乎需要的是策略决策点(PDP)——即评估授权查询("实体X是否被允许执行Y?")的设施。JDK提供了几个这样的设施:
SecurityManager
,特别是其checkXXX
方法组。ProtectionDomain
类,特别是其implies(Permission)
方法。Policy
的关键implies(ProtectionDomain, Permission)
方法。CodeSource
、PermissionCollection
、Permission
和Principal
的implies
方法。AccessControlContext
无关。Shiro本地的授权属性(SNAAs)无论是什么,都适用于整个线程。代码来源并不重要。AccessControlContext
无关。AccessControlContext
有关。管理身份验证的方式由您自行决定。如果您希望继续使用JAAS的javax.security.auth
SPI进行身份验证,则无需建立标准的Subject
作为身份验证结果,而是直接将Shiro特定的结果与线程本地存储相关联。这样,您可以更方便地访问SNAAs,并避免使用AccessControlContext
(并避免可能出现的性能损失)来检索它们。
子类化SecurityManager
,覆盖至少两个checkPermission
方法,使其:
Permission
参数(如有必要)转换为Shiro的PDP(SPDP)可以理解的内容。SecurityException
。接收安全上下文的重载版本可以简单地忽略相应的参数。在应用程序初始化时,实例化并安装(System::setSecurityManager
)您的实现。
Subject
与线程本身关联。SecurityManager
,重写至少两个checkPermission
方法,这次它们应该委托给SPDP和/或被覆盖的实现(后者依次调用当前或提供的访问控制上下文中的checkPermission
)。当要查询哪些权限以及以什么顺序查询时,当然取决于实现。当两者都要被调用时,应首先查询SPDP,因为它可能会比访问控制上下文更快地响应。Policy
,实现implies(ProtectionDomain, Permission)
,使其像上面的SecurityManager::checkPermission
一样,传递一些可理解的域(通常只有它的CodeSource
)和权限参数,但逻辑上不包括SNAAs。该实现应尽可能高效,因为它将在每个访问控制上下文的每个域上一次调用checkPermission
时间。实例化并安装(Policy::setPolicy
)您的实现。根据需要管理身份验证。不幸的是,在这种情况下,处理主题的部分并不像创建ThreadLocal
那么简单。
子类化、实例化和安装执行SecurityManager::checkPermission
和Policy::implies
组合职责的Policy
,如第二个案例中所描述的。
实例化并安装标准的SecurityManager
。
创建一个ProtectionDomain
子类,能够存储和公开SNAAs。
使用SNAAs构造;
实现combine(ProtectionDomain[], ProtectionDomain[])
,使其
与Policy::implies
一样,实现应该高效(例如通过消除重复项),因为每次调用getContext
和checkPermission
AccessController
方法时都会调用它。
在成功身份验证后,创建一个新的AccessControlContext
,其中包装了当前上下文以及自定义DomainCombiner
的实例,依次包装SNAAs。将要执行的代码包装在AccessController::doPrivilegedWithCombiner
调用中,并传递替换的访问控制上下文。
1 相较于使用自定义域和自己的组合器实现,还有一种看似更简单的选择,即将 SNAAs 转换为 Principal
并使用标准的 SubjectDomainCombiner
将它们绑定到当前的 AccessControlContext
的域中(如上所述,或仅通过 Subject::doAs
)。这种方法是否降低了策略的效率主要取决于调用堆栈的深度(访问控制上下文包含多少个不同的域)。最终,您认为可以避免作为域组合器一部分实现的缓存优化将在编写策略时回击您,因此这基本上是您必须在那时做出的设计决策。