如何将JAAS授权检查委托给Shiro?

Den*_*cay 64 java authentication authorization jaas shiro

我正在开发一个需要基于对象的身份验证和授权的服务器端应用程序.我喜欢Shiro的简单性,但为了与JAAS兼容,我编写了一个使用Apache Shiro作为底层机制的LoginModule.

但我的问题是我找不到将JAAS授权检查委托给Shiro的方法.我怎样才能做到这一点?

Uux*_*Uux 4

注意:答案解决了外部授权系统通过标准安全框架与 JVM 集成的一般情况。它不是 Shiro 或 JMX 特定的,因为我对两者都不熟悉。

\n
\n

从概念上讲,您似乎正在追求策略决策点 (PDP),即评估授权查询( “实体 X 是否允许执行 Y?” )的设施。JDK 提供了其中的几个:

\n
    \n
  1. 有效的SecurityManager,特别是它的checkXXX一组方法。
  2. \n
  3. ProtectionDomain,特别是它的implies(Permission)方法。
  4. \n
  5. implies(ProtectionDomain, Permission)有效的关键方法Policy
  6. \n
  7. 其次,、、和implies的方法。CodeSourcePermissionCollectionPermissionPrincipal
  8. \n
\n

任何上述方法都可以被重写,以便以递增的粒度定制概念性PDP的功能。应该指出的是,JAAS 并没有真正带来自己的 PDP(与它的名字所暗示的相反);相反,除了代码来源的原始信任因素之外,它还为域和策略提供了支持基于主体的查询的方法。因此,在我看来,您对保持“JAAS 兼容”的要求基本上转化为想要使用(原始加 JAAS)Java SE 授权模型,又名箱,我怀疑这是否是您想要的。当标准模型被认为太低级和/或性能密集时,往往会采用 Shiro 等框架;换句话说,当授权逻辑不需要评估给定的一组信任因素的每个堆栈帧时,因为这些因素通常是上下文不敏感的。根据我的假设的有效性,需要检查三个主要情况:

\n
    \n
  1. 授权是AccessControlContext独立的。Shiro 原生授权属性 (SNAA),无论它们是什么,都适用于整个线程。代码来源无关紧要。
  2. \n
  3. 代码来源很重要,强制使用沙箱。SNAA 仍然是AccessControlContext独立的。
  4. \n
  5. 代码来源和 SNAA 既相关AccessControlContext相互依赖。
  6. \n
\n
\n

1. 仅基于 SNAA 的授权

\n
    \n
  1. 以您认为合适的方式管理身份验证。如果您希望继续使用 JAAS javax.security.authSPI 进行身份验证,请忘记建立一个标准Subject作为身份验证结果,而是直接将 Shiro 特定的标准绑定到线程本地存储。通过这种方式,您可以更方便地访问 SNAA,并避免必须使用AccessControlContext(并遭受潜在的性能损失)进行检索。

    \n
  2. \n
  3. 子类SecurityManager,至少重写这两个checkPermission方法,以便它们

    \n
      \n
    1. 如有必要,在Permission执行之前将参数翻译成 Shiro 的 PDP (SPDP) 可以理解的内容
    2. \n
    3. 将线程本地 SNAA 和权限委托给 SPDP(SecurityException如果 SPDP 信号访问拒绝,则抛出一个信号)。
    4. \n
    \n

    安全上下文接收重载可能会简单地忽略相应的参数。在应用程序初始化时,实例化并安装 ( System::setSecurityManager) 您的实现。

    \n
  4. \n
\n
\n

2. 混合授权,将代码来源与上下文不敏感的 SNAA 相结合

\n
    \n
  1. 以您认为合适的方式管理身份验证;再次将 Shiro 特定Subject与线程本身关联起来。
  2. \n
  3. Subclass SecurityManager,至少重写这两个checkPermission方法,这一次它们委托给 SPDP 和/或重写的实现(相应地checkPermission,相应地调用当前或提供的访问控制上下文)。对于任何给定的许可,应该咨询哪些(哪些)以及以什么顺序当然取决于实现。当两者都被调用时,应该首先查询 SPDP,因为它可能比访问控制上下文响应得更快。
  4. \n
  5. 如果 SPDP 要另外处理授予源自某个位置和/或一组代码签名者的代码的权限的评估,您还必须子类化Policy,实现implies(ProtectionDomain, Permission)这样的,像SecurityManager::checkPermission上面一样,它传递域的一些可理解的表示(通常是仅向 SPDP 提供其CodeSource) 和许可参数,但逻辑上不是SNAA。实现应该尽可能高效,因为每次每个域每个访问控制上下文都会调用一次它checkPermission。实例化并安装 ( Policy::setPolicy) 您的实现。
  6. \n
\n
\n

3. 混合授权,将代码来源与 SNAA 相结合,两者都是上下文相关的

\n
    \n
  1. 以您认为合适的方式管理身份验证。不幸的是,主题处理部分并不像ThreadLocal在本例中创建 a 那样微不足道。

    \n
  2. \n
  3. 子类化、实例化并安装 ,执行和Policy的组合职责,如第二种情况中单独描述的。SecurityManager::checkPermissionPolicy::implies

    \n
  4. \n
  5. 实例化并安装标准SecurityManager.

    \n
  6. \n
  7. 创建一个ProtectionDomain能够存储和公开 SNAA 的子类。

    \n
  8. \n
  9. 作者1a那个DomainCombiner

    \n
      \n
    1. 是用 SNAA 构建的;

      \n
    2. \n
    3. 实施combine(ProtectionDomain[], ProtectionDomain[])这样的

      \n
        \n
      1. 它将第一个(“当前”上下文)数组参数的域替换为自定义实现的等效实例;
      2. \n
      3. 然后将第二个(“分配”或“继承”上下文)参数(如果有)按原样附加到前一个参数;最后
      4. \n
      5. 返回连接。
      6. \n
      \n
    4. \n
    \n

    与 一样Policy::implies,实现应该是高效的(例如,通过消除重复项),因为每次getContext和 时都会调用它checkPermission AccessController都会调用它。

    \n
  10. \n
  11. 身份验证成功后,创建一个新的AccessControlContext来包装当前的实例以及自定义的实例DomainCombiner,进而包装 SNAA。包装要在该点“内”之外\n执行的代码AccessController::doPrivilegedWithCombiner,同时传递替换访问控制上下文。

    \n
  12. \n
\n
\n

1\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0除了使用自定义域和您自己的组合器实现之外,还有一种看似更简单的替代方法,即将 SNAA 转换为Principals,并使用标准SubjectDomainCombiner将它们绑定到当前的AccessControlContext域(如上所述,或简单地通过Subject::doAs)。这种方法是否会降低策略的效率主要取决于调用堆栈的深度(访问控制上下文包含多少个不同的域)。最终,您认为可以避免作为域组合器的一部分实现的缓存优化将在编写策略时对您产生影响,因此这本质上是您此时必须做出的设计决策。

\n