当前位置: 首页 > 知识库问答 >
问题:

JAAS 登录模块中的用户主体 - 作为单独的主体还是作为 Bean 属性的额外属性?

漆雕稳
2023-03-14

我已经实现了一个 JAAS 登录模块,它与 Spring 4 和 Struts 2.3 配合使用效果很好。这个相同的登录模块也可以通过 Tomcat 8.0/8.5 中的 Servlet 过滤器调用,以对 Spring 框架之外的 Servlet 请求进行身份验证和授权。

< code>LoginModule使用< code > Java . security . ACL . group 的简单实现,并使用< code > Java . security . principal 的两个简单实现来分隔用户和角色。我所说的“简单”是指满足接口的最小实现。

“User”实现将< code>name属性映射到一个唯一的用户名(实际上是一个电子邮件地址)。由于电子邮件地址是唯一的,但可能会改变,帐户数据库包含一个唯一的帐户标识符(GUID ),用于分配组、角色和记录服务请求(同时也匿名化我们的用户)。在我的模型中,< code>AccountIdentifier有自己的类。本质上,我有两个惟一的帐户标识符,但是由于电子邮件地址需要提供给< code>LoginModule进行身份验证,所以它最终成为用户主体的基础。

帐户标识符当前没有传播到登录模块中的主题,但现在我需要它来记录服务请求。

我认为通过主题提供帐户标识符有两种方式,但我不确定JAAS的最佳实践是什么:

  1. 扩展我当前的“User”Principal实现,以包括在LoginModule.commit()LoginModule()期间设置的“accountIdentifier”属性
  2. AccountIdentifier作为一个单独的主体来实现,该主体在 0.05主题中

第一个选项是最简单的,但这似乎也违背了将个人身份信息与账户隔离的目的(这是我需要做的事情,以满足即将到来的欧盟数据保护法要求)。

我是否应该在Subject中添加“User”主体(包含电子邮件地址的主体)?

共有1个答案

柴琦
2023-03-14

JAAS和Servlet规范在身份验证和用户原则方面存在一些不兼容性。正因为如此,Spring使用了不同于Tomcat的JAAS集成方法。

这个答案记录了一种以同时容纳Tomcat和Spring的方式实现JAAS登录模块的综合方法。

为清楚起见,从问题中复制了实现用户原则的两个选项:

    < li >扩展我当前的“User”< code > Principal 实现,以包含在< code > log in module . commit()期间设置的“accountIdentifier”属性。 < li >将< code>AccountIdentifier作为单独的< code >主体来实现,该主体在< code > log in module . commit()期间添加到< code>Subject中。

选项 1 具有将不同形式的个人身份信息连接在一起的不幸副作用,这在某些情况下可能违反欧洲 GDPR 法规(会话可以序列化到磁盘上,并且此信息会随之而来)。

选项2分离出个人身份信息,但必须以克服Servlet规范和Tomcat的JAAS实现中的几个限制的方式实现。

下面详细描述了这些限制,粗体部分总结了要点。

Tomcat 8.5 JAAS领域文档指出:

使用JAASRealm使开发人员能够将几乎任何可以想象的安全领域与Tomcat的CMA相结合。

但接着又说:

虽然在JAAS中没有指定,但是您应该创建单独的类来区分用户和角色,扩展< code > javax . security . principal ,以便Tomcat可以判断从您的登录模块返回的主体中哪些是用户,哪些是角色(请参见< code > org . Apache . catalina . realm . JAAS realm )。无论如何,返回的第一个主体总是被视为用户主体。

注意,上面的Tomcat文档使用了短语“用户主体”。尽管JAAS API建议将用户和角色实现为扩展javax.security.Principal的不同类,但这与Servlet规范不兼容,因为http Servlet request . getuser Principal()只允许返回单个主体:

返回一个< code > javax . security . principal 对象,其中包含当前已通过身份验证的用户的名称。如果用户尚未通过身份验证,该方法将返回null。

对上述文档的严格阅读表明,它应该包含“...当前认证用户的名字”,但是为了满足我最初的目标,我把它解释为“...被认证主体的任何名称或标识符”。这更接近于com . sun . security . auth . user principal文档(即“由用户名或帐户名标识的用户主体”)。

由于Tomcat的< code>JAASRealm和Servlet规范的< code>HttpServletRequest中的上述限制,如果帐户标识符要通过< code>ServletFilter(它只能访问当前会话、请求和响应)传播到请求中,那么它必须包含在第一个< code>Principal中(因此,原始问题中的选项1将满足此要求,或者只有在选项2首先出现并且我不需要原始用户名时,才满足此要求)我相信我真正需要的是帐户标识符,所以我现在坚持使用第二个选项,我将“EmailAddressPrincipal”传递给< code>MyLoginModule,并通过< code>Subject接收一个“AccountIdentifierPrincipal”(即< code>MyLogin.commit()将“AccountIdentifierPrincipal”添加为第一个主体)。

JAASRealm 文档实际上在原理的确切顺序方面略有矛盾,这取决于您正在阅读的章节:

当这个领域遍历Subject.getPrincipals()返回的主体时,它将识别与“用户类”列表匹配的第一个主体作为该用户的主体

与。

无论如何,返回的第一个主体始终被视为用户主体。

本质上,如果我要创建一个ServletFilter来模仿JAASRealm所做的事情,身份验证将如下所示(特别注意迭代器):

final LoginContext loginContext = new LoginContext(MyLoginModule.JAAS_REALM, new DefaultCallbackHandler(username, password));
loginContext.login();
final Subject subject = loginContext.getSubject();
request.getSession().setAttribute("AUTH_USER_PRINCIPAL", subject.getPrincipals(AccountIdentifierPrincipal.class).iterator().next());
request.getSession().setAttribute("AUTH_ROLE_PRINCIPALS", subject.getPrincipals(MyRolePrincipal.class));

不幸的是,这与 javax.security.auth.Subject 的构造函数直接冲突,该构造函数要求将 java.util.Set 用作主体的支持集合。此外,Set.迭代器 () 文档指出:

元素不按特定顺序返回(除非此集是某个提供保证的类的实例)。

我们最早对主题的访问是在LoginModule.initialize()方法中,不幸的是,这是在LoginContext的内部某处调用的东西(我认为)。这意味着我们无法控制Set的确切子类,该子类用作校长的后备集合,因此无法控制它们的排序。当它到达ServletFilter时,它是一个同步集,因此甚至不清楚原始类是什么,或者是否发生了重新排序

所有这些都表明,为了让< code>JAASRealm按预期工作,只能提供一个用户主体。在中间层的任何地方都没有接口来清楚地建立< code > Subject < code > principal 的顺序。

使用JAASRealm时,在提交期间,应仅将声明的User类型的一个主体添加到主题

使用< code>JAASRealm时,避免使用多个用户类名。

违反上述两个规则可能会导致未定义和/或不一致的行为。

出于选项2的考虑,我避免使用< code>JAASRealm,因为根据上面所有的文档,它并不忠实于JAAS。这使我回到了纯粹的< code>ServletFilter方法。

主题包含授权所需的所有内容:多个用户主体、角色和 ACL 组。不幸的是,这个类只能部分序列化,这意味着我不能只是将类包装为主体并返回它。

为了满足Spring的DefaultJaasAuthenticationProvider,需要实现一个AuthorityGranter来将< code>Principal映射到角色名——它提供了对如何执行映射的完全控制。

因为< code>AuthorityGranter在Spring框架之外不可用,所以我还实现了一个< code>ServletFilter,它使用类似的方法为我的非Spring webapp映射角色。我暂时使用< code > http servlet requestwrapper 从会话属性(在身份验证期间存储在会话中)读取主体和角色,并覆盖getUserPrincipal和isUserInRole。最后,我将重新访问JAASRealm,看看它是否包含任何处理这部分的功能,但我还没有完全实现。

 类似资料:
  • 我有两个表审查和bedrijfsgegevens。Review表有一个主键(reviewId)和一个外键(bedrijfId)。BedrijfId是Bedrijfsgegevens表中的主键(作为Id)。我遇到了这个问题,因为我试图在Review表中添加一个bedrijfId,而Bedrijfgegevens表中不存在这个行,因此得到了一个异常。 我尝试使用@manytoone和@joincolu

  • 我在API上使用Vertx框架对JWT令牌进行授权。在用户被授权并且令牌被解密之后,我想要访问令牌的内容,尤其是令牌中的“userId”字段。 最初,我是用作为作为以下内容来访问它的:

  • 我使用的是Netbeans IDE 8.0.2和eclipselink 2.5.2。下面是打开连接时出现的异常,问题是这种情况并不是每次都发生。异常“Departmento”中描述的实体完全遵循其他类的模式,即我们的系统已经包含大约500个实体类,只有在新的类中才发生此异常。这个实体是由Netbeans的“实体类从数据库”选项生成的,并添加到持久化XML... 由:javax.Persistenc

  • 我必须写一个程序,它将接受Hashmap格式的body。我已经用Hashmap值创建了HttpEntity 我面临以下例外: 异常线程"main"org.springframework.web.client.RESTClientExcture: No HttpMessageConzer for[java.util.HashMap]atorg.springframework.web.client.R

  • 当我使用Office 365的配置api时(通过连接msolservice或https://provisioningapi.microsoftonline.com/provisioningwebservice.svc)我正在使用用户名和密码。当我连接时,我会自动使用powershell或通过web服务和office365部署(如联合/解除联合本地域) 我面临的问题是密码最终会过期,我的服务会中断,

  • 我正在尝试为一个名为Zulip的开源项目构建python api,我一直遇到下面截图所示的相同问题。 我正在运行python3,我的pip版本是10.0。0.正在讨论的文件是而混乱的代码是当属性可用于安装软件包。 现在,我知道这个构建应该成功,因为它是一个开源项目,但是我已经尝试了几个小时来修复关于的依赖问题。 任何帮助将不胜感激。