WebSphere Application Server中的高级认证

转载自: https://blog.csdn.net/cuxiong8996/article/details/107155123

摘自IBM WebSphere开发者技术期刊 。

过去,IBM WebSphere Application Server具有严格的身份验证模型,使其难以支持复杂或异常的需求。 最近,通过基于Java身份验证和授权服务(JAAS)的新的,高度可定制的身份验证框架解决了这种情况,该框架扩展了一些功能,这些功能专门解决了在分布式应用程序服务器环境中管理用户真实性和特权的要求。

为WebSphere Application Server中的JAAS支持定义的新框架包括:

  • 定义明确的界面,用于更改用户主题
  • 增强的可信协会拦截器(TAI)支持
  • WebSphere Application Server登录过程的明确文档
  • 声明对WebSphere Application Server的完整用户凭证的能力(包括组信息)
  • 在分布式环境中复制主题

加上对安全编程模型的许多有益扩展。

本文将详细介绍这些新功能。 下一节将提供一些有关WebSphere Application Server认证过程的基础知识和JAAS概述,然后本文的其余部分将描述这些功能与认证有关的最重要方面。

认证概述

您应该使用哪个版本?

本文中的信息基于WebSphere Application Server V6.0.1.2。 本文描述的功能首先出现在分布式平台上的WebSphere Application Server V5.1.1中,并且作为更新的一部分,也可用于z /OS®上的WebSphere Application Server V5.1。 但是,由于重要的修复程序已在6.0.1.2版中应用,我们强烈建议您使用该版本。

首先,让我们看一下WebSphere Application Server支持认证的方式 :

  1. 在WebSphere Application Server登录期间,必须提供一些信息来证明用户的真实性。 这可以是诸如用户ID和密码,来自SSL会话的X509证书或来自浏览器的单一登录令牌之类的信息。

  2. 一旦此信息经过认证或验证(认证通过;令牌通过认证),便会使用WebSphere Application Server认证和主体生成JAAS主题。

    • WebSphere Application Server凭证是主题的公共凭证集中的WSCredential实现。
    • WebSphere Application Server主体是该主体的主体集中的WSPrincipal实现。
  3. 当然,将创建定制类型以包含WebSphere Application Server用于跟踪用户身份的定制信息。

  4. 在登录线程上访问资源时,将在进行身份验证的服务器中使用该主题以做出授权决策。

除了身份验证(要求请求实体提供其身份证明)外,WebSphere Application Server还支持身份声明 。 这是一种轻松的身份验证形式,实际上并不需要身份证明,而是基于与与证明所声明身份的实体的信任关系来接受身份。

图1说明了WebSphere Application Server中可用的认证方法,其中我们区分了两种主要的认证情况:

  • Web客户端身份验证。
  • EJB客户端认证。

图1. WebSphere Application Server认证方法

图1. WebSphere Application Server认证方法

Web认证

通过设计将安全性构建到应用程序中

通过IBM Bluemix中的安全服务,保护工作负载免受最新威胁的威胁,简化访问控制并有效地遵守法规要求。

注册免费试用

Web客户端进行身份验证的最常见方法是提供用户ID和密码,可以使用HTTP基本身份验证或基于表单的身份验证来接受用户ID和密码。 WebSphere Application Server获取此信息,在注册表中查找用户的唯一ID(例如LDAP的DN),然后根据注册表验证密码。 对于LDAP,将执行ldap_bind。

Web客户端也可以使用客户端证书进行身份验证。 与任何SSL系统一样,客户端证书身份验证是在SSL连接终止时完成的。 因此,Web服务器而不是WebSphere Application Server负责执行客户机证书认证。 证书认证完成后,WebSphere Application Server Web服务器插件将客户机证书信息转发到应用程序服务器,然后该服务器从证书中提取信息并在注册表中查找用户(因此,这实际上是来自Web服务器到WebSphere Application Server)。 请注意,当Web服务器执行客户端证书身份验证时,Web服务器插件仅声明该信息从Web服务器到应用程序服务器。 因此,应该通过相互认证的SSL保护从Web服务器到WebSphere Application Server的连接。 否则,任何直接访问应用程序服务器Web容器的客户端都可以轻松伪造证书信息并损害安全性。

用于注册表查找的信息是可定制的,并且如果使用WebSphere Application Server定制注册表界面开发了定制注册表,则可以使其完全灵活。 进行身份验证后,将创建一个单点登录(SSO)令牌并将其作为cookie发送给浏览器-等效于WebSphere Application Server先前版本中的LTPA令牌-并且安全凭证被缓存。安全运行时。

EJB客户端认证

EJB客户端可以使用密码或证书进行身份验证。 在基于密码的身份验证的情况下,客户端运行时负责获取用户ID和密码,并将其发送到服务器以根据注册表进行验证。 在任何一种情况下,如果确定身份验证有效,则将建立CSIv2会话并将其用于将来的请求。 与Web客户端身份验证一样,将创建一个JAAS主题。 与Web客户端不同,该主题与CSIv2会话相关联,而不是放在安全高速缓存中。

缺省情况下,WebSphere Application Server客户机运行时使用图形对话框提示用户ID和密码(如果需要)。 可以通过编辑sas.client.props文件来控制此行为,您甚至可以在其中指定用户ID和密码。 但是,在获取用户ID和密码后,建议客户端在应用程序控制下使用JAAS登录API以某种适当的方式进行身份验证。

信任关联拦截器

HTTP客户机还可以使用信任关联拦截器(TAI)将标识信息传递给WebSphere Application Server。 TAI接口提供了一种机制,WebSphere Application Server通过该机制可以使外部组件对用户进行身份验证,然后向WebSphere Application Server Web容器声明身份。 您可以开发自定义的TAI或使用已经市售的几种。 这些TAI通常与Web身份验证代理服务器结合使用,例如IBM的Tivoli®Access Manager或Netegrity的SiteMinder。 这些产品对用户进行身份验证,然后仅将最终用户的身份告知WebSphere Application Server。 通常,这是通过代理服务器将用户的ID和一些其他可验证信息发送到应用程序服务器来完成的。 TAI提取此信息,然后将用户的ID(或可选的主题)返回给WebSphere Application Server,然后由WebSphere Application Server按正常方式查询注册表,但不验证用户的密码。 (如果在注册表中找不到用户ID,则断言当然会失败。)这提供了一种强大的机制,使WebSphere Application Server能够参与单个登录域。

高级TAI不仅可以断言用户的ID。 实际上,他们可以声明一个完整的身份,包括支持授权所需的所有组成员身份。 在这种情况下,WebSphere Application Server将不需要查询用户注册表。

内部认证

应用程序代码以及WebSphere Application Server本身也可以从流程中进行身份验证,实质上是即时创建经过身份验证的主题。 为此,将使用标准的JAAS登录API,并遵循在其他方案中使用的相同方法:针对注册表对提供的用户ID和密码进行验证,如果验证成功,则将创建JAAS主题和身份验证令牌。 尽管可能并不明显,但这意味着当WebSphere Application Server服务器对其自身进行身份验证时,它们使用与用户级身份验证相同的注册表进行身份验证。

JAAS概述

本节提供有关JAAS概念的一些基本入门资料,特别是使用登录模块提供可插入身份验证的方式。 读者应该熟悉JAAS规范。

JAAS简介

WebSphere Application Server大量使用了JAAS编程模型。 JAAS是一个标准Java框架,用于执行许多与安全性相关的任务,包括登录,自定义身份验证和(通过Java 2扩展)授权。 WebSphere Application Server支持使用JAAS进行登录和定制认证(具有本文所述的限制)。 JAAS公开了供应用程序使用的应用程序级编程接口(API),以及用于其功能提供者的服务编程接口(SPI)。 该模型实现了将应用程序与服务提供商隔离的目标,从而实现了跨系统平台的可移植性。 此外,JAAS遵循可插拔认证模块(PAM)认证模型,因此服务提供商可以通过管理配置过程完全插拔。

身份验证有时涉及多个身份验证系统。 为此,JAAS不仅是可插入的,而且也是可堆叠的。 这意味着可以配置一系列的一个或多个机制来驱动身份验证过程。 图2是JAAS模型的高级表示。

图2. JAAS认证框架

图2. JAAS认证框架

JAAS使用主题的概念来定义用户。 主题是在初始身份验证时创建的,实际上只是用户信息的容器。 它包含主体和凭证数据。 身份验证由分组为登录配置的登录模块执行,从而提供上述可堆叠的身份验证机制。 登录模块可以将数据填充到主题中。 在WebSphere Application Server中,这将包括WSPrincipal和WSCredential对象。 WSPrincipal基本上是Java主体,用于定义Java中的实体,例如用户,组织或登录ID。 WSCredential定义将用于授权的安全性信息,例如组成员身份。

JAAS控制流程

如上所述,JAAS登录过程提供对编排为登录配置的一系列登录模块的访问。 JAAS调用模型由调用者(例如WebSphere Application Server容器运行时)组成,交替调用JAAS组件,直到调用所有已配置的模块或遇到故障为止。 图3显示了在调用者和JAAS模块之间交替控制流的这种类似于跷跷板的模式。

图3. JAAS调用模型

图3. JAAS调用模型

每个JAAS登录模块的效果由具有以下值之一的配置属性驱动:

  • 必需:必须成功使用LoginModule。 无论身份验证成功与否,都将继续使用后续的LoginModule列表进行身份验证。

  • 必需:必须成功使用LoginModule。 如果成功,身份验证将从LoginModule列表继续进行。 如果失败,则控制权立即返回到应用程序(身份验证不会在LoginModule列表中继续进行)。

  • 足够:不需要LoginModule即可成功。 如果成功,则控制权立即返回到应用程序(身份验证不会在LoginModule列表中继续进行)。 如果失败,则身份验证将从LoginModule列表继续进行。

  • 可选:不需要LoginModule即可成功。 如果成功或失败,身份验证仍继续沿LoginModule列表进行。

(缺省情况下,WebSphere Application Server登录模块具有必需的语义。)

现在,我们将通过显示JAAS模块的调用顺序来扩展图3的流程,然后讨论通过控制器/调用程序交换的数据构造。 这将涉及对核心JAAS对象的操作,这些对象是:

  • 学科
  • LoginContext
  • 登录模块

对于JAAS调用者而言,主题和LoginContext对象是可见的,而对于LoginModule,则不是。 也就是说,它仅受配置限制。 要验证实体(我们将其称为主体),请执行以下步骤:

  1. 应用程序实例化LoginContext。

  2. LoginContext咨询登录配置以加载属于该配置的所有登录模块。

  3. 该应用程序调用LoginContext的login方法。

  4. 登录方法根据上述语义调用加载的登录模块。 每个登录模块都尝试对主题进行身份验证。 成功后,登录模块会将相关的主体和凭据与代表正在验证的主题的Subject对象相关联。

  5. LoginContext将身份验证状态返回给应用程序。

  6. 成功后,应用程序将从LoginContext中检索主题。

跨登录模块调用的数据流

如上所述,通过实例化单独的LoginContext对象及其对应的LoginModule对象,可以执行JAAS编程模型中主题的身份验证。 这两个构造封装了要认证的主体的认证数据。

在调用每个登录模块之前,将使用主题,共享的登录模块状态,特定于登录模块的选项以及可能的CallbackHandler对其进行初始化。 身份验证成功后,将使用相关凭据更新主题。 因此,由主题封装的数据表示通过配置的登录模块传递的第一个构造,而共享状态表示第二个此类数据元素。 这两个对象使一个登录模块的作用对于列表中的下一个模块可见。

登录模块使用可选的CallbackHandler来收集有关环境和用户的身份验证信息,包括身份和该身份的拥有证明。 此过程可能涉及与最终用户的交互。 但是,这对于服务器端回调是不可行的。 在服务器端,此信息是从传入的客户端请求或环境本身获取的。 JAAS用法部分中讨论了WebSphere Application Server的回调处理程序。

认证插件

WebSphere Application Server认证过程是完全可插入的。 通过在大多数关键步骤提供定制代码的插入点,可以大量定制WebSphere Application Server认证过程。 可以开发代码以向主题添加定制信息,在登录过程中需要其他身份验证信息,甚至可以通过向WebSphere Application Server声明完整的用户凭证来绕过正常的注册表用法。

可以通过两种方式来实现对WebSphere Application Server认证模型的这些扩展:

  • 首先,大多数身份验证过程都是围绕JAAS登录模块构建的,因此可以在IBM提供的登录模块之前,之后或之间插入自定义登录模块(但是,不得删除IBM模块)。

  • 其次,TAI接口支持基于Web的身份验证的完整凭据的声明。

WebSphere Application Server提供了一组带有登录模块和回调的标准登录配置,这些配置在各种情况下用于实现认证。 这些模块和回调处理程序是根据特定的身份验证情况定义和可用的。 支持大多数需求的主要配置是Web入站,RMI入站和RMI出站方案。 我们将对每个细节进行详细讨论,但是首先我们将看一下标准的WebSphere Application Server登录模块。

标准登录模块

我们感兴趣的是在WebSphere Application Server中预定义的三个登录模块,可与WEB_INBOUND,RMI_INBOUND,DEFAULT和RMI_OUTBOUND配置一起使用:

  • com.ibm.ws.security.server.lm.ltpaLoginModule
    使用用户提供的哈希表或默认注册表访问权限创建凭据。

  • com.ibm.ws.security.server.lm.wsMapDefaultInboundLoginModule
    根据登录类型,使用凭证或令牌实例化主题。

  • com.ibm.ws.security.server.lm.wsMapCSIv2OutboundLoginModule
    将主题转换为令牌,以便通过IIOP进行出站传输; 仅在RMI_OUTBOUND中使用。

您可以在IBM登录模块之前或之后放置自定义登录模块。 放置它们的位置将取决于您要实现的目标:

  • 如果要声明身份信息并绕过针对用户注册表进行身份验证的标准WebSphere Application Server登录模块行为,那么必须将登录模块放在ltpaLoginModule之前。

  • 但是,如果您的登录模块需要使用由WebSphere Application Server登录模块创建的信息(例如,您需要查看经过身份验证的用户才能向该主题添加额外的信息),那么您可以将登录模块放在IBM模块之后。

也可以将您的定制模块放在两个IBM登录模块之间,但这很少需要。 如果您编写一个RMI_OUTBOUND登录模块,那么在IBM wsMapCSIv2OutboundLoginModule之前执行登录模块至关重要,这就是一种情况。

系统登录配置

WebSphere Application Server现在定义了四个系统登录配置,这些配置用于与安全性相关的特定情况:

  • WEB_INBOUND
  • RMI_INBOUND
  • RMI_OUTBOUND
  • 默认。

(还有一些用于其他目的的应用程序登录配置,例如J2EE Con​​nector身份验证,这里将不再讨论。)

WEB_INBOUND

图4显示了WEB_INBOUND配置的身份验证流程的简化视图。

图4. Web入站流程

图4. Web入站流程

顾名思义,此配置旨在用于认证基于Web的(HTTP)通信。 共有三种基本方案:

  • 如果用户已经通过身份验证(意味着请求中存在SSO令牌)并且主题在本地安全缓存中可用,则将绕开TAI和所有登录模块,并将控制权直接传递到Web容器中。

  • 如果有一个SSO令牌可用,但是该主题不在本地安全性高速缓存中,那么WebSphere Application Server将尝试从另一台服务器获取用户的主题(我们将在稍后说明)。 如果成功,则执行传播登录。 (传播将在后面的部分中讨论。)将调用登录模块,并期望该登录模块可以验证主题。 如果WebSphere Application Server无法获得主题,那么事情将变得棘手。 缺省情况下,WebSphere Application Server将执行初始登录(在下一个项目符号中),但是可以更改此行为。

  • 如果没有SSO令牌可用,则执行初始登录。 首先,将调用TAI(如果存在)。 调用TAI后,将调用登录模块。 TAI可以创建供登录模块使用的主题。

调用登录模块时,将同时调用必需的登录模块和所有自定义登录模块。 它们的调用顺序由登录配置通过管理方式确定。 当然,您可以决定编写自定义的TAI和自定义的登录模块,但这不是一个理想的选择。

如图4所示,请注意,TAI可以为登录模块提供自定义主题。 自定义登录模块还可以自定义主题。 不久,我们将更详细地介绍登录模块和TAI可以做什么。

RMI_INBOUND

图5显示了RMI_INBOUND配置中认证流程的简化视图。

图5. RMI入站流程

图5. RMI入站流程

顾名思义,此配置旨在用于验证RMI / IIOP请求; 也就是说,绕过Web容器并直接调用EJB的请求。 这些可能是来自独立EJB客户端或另一个应用程序服务器的请求。

共有三种基本方案:

  • 如果用户已经通过身份验证,因此已经建立了CSIv2会话,那么将绕过所有登录模块,并将控制权直接传递到EJB容器中。

  • 如果用户在调用方已经具有经过身份验证的上下文,则配置中的登录模块堆栈将在传播登录中执行(初始登录和传播登录之间的差异将在后面说明),作为用户身份验证数据(已知作为令牌)从一台服务器传播到另一台服务器。 所需的WebSphere Application Server登录模块以及添加到配置中的所有定制登录模块都将被调用。

  • 如果用户尚未通过身份验证,则登录模块将像以前一样被调用,但是这次是在初始登录模式下进行的。 登录模块负责验证用户的身份验证数据并创建主题。

RMI_OUTBOUND

图6显示了RMI_OUTBOUND配置中身份验证流程的发生方式。

图6. RMI出站流

图6. RMI出站流

顾名思义,此配置旨在用于对远程EJB容器的调用上的与身份验证相关的特殊处理,该处理通常涉及某种类型的凭据映射处理。 您可以对远程服务器执行身份声明,而无需登录模块,但是如果远程服务器不与身份验证服务器共享注册表,则可能需要使用登录模块将现有的用户身份信息映射到凭据。对于该服务器有效。

  • 如果CSIv2会话已经存在,则直接调用远程容器,并且不执行登录模块。

  • 否则,将执行配置中的登录模块堆栈。 这将包括必需的WebSphere Application Server登录模块以及添加到配置中的所有定制登录模块。

本文将不再讨论此配置。

其他配置

WebSphere Application Server中还有其他定义的登录配置。 如果没有上述配置,则DEFAULT登录配置可以处理这种情况。 其中包括来自管理客户端的SOAP请求和JMX管理员身份验证请求。 可能还会有旧版登录配置,例如SWAM,LTPA和LTPA_WEB。 但是,从WebSphere Application Server V5.1.1开始,不再使用这些功能。

另外,有两种与Web服务相关的登录配置,wssecurity.signature和wssecurity.IDAssertion,本文将不介绍。

TAI用法

了解JAAS和WebSphere Application Server认证的基础知识之后,我们现在将讨论如何更详细地定制WebSphere Application Server认证过程。 我们将以自定义TAI开始,因为它们最容易理解和使用-也是自TAI支持基于Web的单点登录以来最常用的方法。

TAI:将身份信息声明给WebSphere Application Server

如果用户已经通过WebSphere Application Server以外的某种认证系统进行了认证,则可以将用户的身份信息通知给WebSphere Application Server,而不是要求用户重新认证。 这称为身份声明 。

WebSphere Application Server长期以来一直支持将基于Web的身份声明作为TAI界面的一部分。 从WebSphere Application Server V5.1.1开始,有两个TAI接口。 保留了将用户ID返回到WebSphere Application Server的传统TAI方法,但是现在可以使用一种新的,功能更强大的方法。 定制开发的代码不仅可以声明用户的ID,还可以声明整个用户身份(用户ID,唯一ID和组信息)。 如果这样做,WebSphere Application Server将不会与UserRegistry联系以获取其他用户信息。 在已知用户组信息的情况下,这可以提高性能,但是更重要的是,这使应用程序可以灵活地根据UserRegistry中的静态信息以外的标准来确定用户的组成员身份。 TAI中的另一个新功能是对多阶段协商过程的支持,该过程使TAI能够从客户端请求断言身份的其他信息。

支持主题断言的高级TAI接口是com.ibm.wsspi.security.tai​​.TrustAssociationInterceptor。 请注意,这与旧的(仍受支持)com.ibm.websphere.security.TrustAssociationInterceptor不同。

如上所述,此高级TAI接口支持多阶段协商的身份验证过程(例如,某些系统需要将质询响应协议返回给客户端)。 此新界面中的两个关键方法是:

  • 公共布尔isTargetInterceptor(HttpServletRequest req)
    如果此TAI应该处理此请求,则此方法将返回true;否则,此方法将返回true。 false告诉WebSphere Application Server忽略此TAI。

  • 公共TAIResult协商有效和建立信任(HttpServletRequest req,HttpServletResponse res)
    此方法返回一个TAIResult对象,该对象指示正在处理的请求的状态。 如果需要,可以修改HTTP响应对象。

TAIResult类具有三种用于创建TAIResult的静态方法,所有这些方法始终将int作为第一个参数。 该参数应为有效的HTTP请求返回码,并可以通过以下两种方式之一进行解释:

  • HttpServletResponse.SC_OK告诉WebSphere Application Server TAI已完成其协商。 然后,WebSphere Application Server将使用TAIResult中的信息来创建用户身份。

  • 任何其他值指示WebSphere Application Server应该返回TAI输出(已将其放置在HttpServletResponse中)并将其返回到Web客户机(或更可能是返回到认证代理)。 通常,这将导致Web客户端提供有关将来请求的其他信息,并再次调用TAI。

创建的TAIResults具有以下含义:

  • 公共静态TAIResult create(int status);
    仅指示WebSphere Application Server的状态。 由于不提供身份信息,因此状态不应为SC_OK。 通常,这将用于通知断言客户端TAI需要其他信息。 TAI最有可能修改了HttpServletResponse对象,以将一些有用的信息发送回调用方。

  • 公共静态TAIResult create(int status,String主体);
    指示状态和该用户的用户ID或唯一ID。 WebSphere Application Server将通过查询注册表以获取所有必需的用户信息来创建凭证。 状态必须为SC_OK。

  • 公共静态TAIResult create(int status,String主体,Subject主题);
    指示状态,该用户的用户ID或唯一ID以及自定义主题。 如果主题包含完整的用户哈希表(请参阅下一节有关创建自定义主题的内容),则主体将被忽略。 主题的内容将成为最终用户主题的一部分。 状态必须为SC_OK。

Java文档中讨论了TrustAssociationInterceptor接口上的一些其他方法。 这些用于初始化,关闭以及用于识别WebSphere Application Server的TAI。

在这种框架下,定制的TAI可以完成许多不同的事情。 接下来是许多简单的示例,稍后将提供一个更完整的示例。 为了清楚起见,这些示例中的大多数都未显示错误处理。

仅声明用户的TAI

这是一个断言用户ID的greetingValidateandFoundTrust的简单示例:

清单1

 
  1. TAIResult negotiateValidateandEstablishTrust

  2. (HTTPServletRequest req, HTTPServletResponse response) {

  3.  
  4. String userid = //get from request somehow

  5. if ( didn't work )

  6. throw new WebTrustAssociationFailedException("a reason why");

  7. return TAIResult.create(HTTPServletResponse.SC_OK, userid);

  8. }

在这种情况下,仅提供用户的用户ID。 WebSphere Application Server将访问注册表以检索所有其他用户信息,例如组成员身份。

为了指示认证失败(并导致WebSphere Application Server使用户认证失败),必须抛出WebTrustAssociationFailedException。

谈判的TAI

此示例显示如何指示需要额外的协商,以及如何返回用户标识:

清单2

 
  1. TAIResult negotiateValidateandEstablishTrust

  2. (HTTPServletRequest req, HTTPServletResponse response) {

  3.  
  4. //determines phase by examining request

  5. if (phase one) {

  6. ... Alter response object appropriately...

  7.  
  8. //Indicate not done to WebSphere Application Server.

  9. //Return code is client specific

  10. return TAIResult.create (HTTPServletResponse.SC_CONTINUE);

  11. } else {///done

  12. String userid = //get from request somehow

  13. return TAIResult.create(HTTPServletResponse.SC_OK, userid);

  14. }

  15. }

创建一个自定义主题

WebSphere Application Server还提供了创建定制主题的功能。 实际上,这可以通过TAI或自定义登录模块中的用户代码来实现。 在详细了解如何在TAI中实现此目标之前,我们需要澄清一下,实际上有两个非常不同的事情可以描述为“创建自定义主题”。 更简单的含义只是将定制属性添加到标准主题,该主题由WebSphere Application Server使用其常规方法创建。

要记住的一件事是,如果您创建包含自定义属性的自定义主题,则创建该主题的代码可能需要在每个应用程序服务器上。 这是因为任何应用程序服务器都可以将主题创建为身份验证的一部分。 更重要的是,每个应用程序服务器都可能需要反序列化主题,因此为了使其有意义,它必须有权访问主题的Java类定义。 如果目标服务器没有定制类,则反序列化将失败,并且WebSphere Application Server将仅丢弃定制信息。

“自定义主题”的第二个含义是通过提供包含用户身份信息的自定义哈希表来创建具有自定义凭据的主题。 在这种情况下,用户代码提供WebSphere Application Server生成主题所需的所有身份信息。

With either type of custom subject, it is important to understand the implications in a clustered environment. This will be covered in detail later in the sections on JAAS usage and propagation , but the important thing to be aware of is that if the subject you create is modified in such a way that WebSphere Application Server cannot recreate it using its own default mechanisms, then you must add a custom cache key to the subject. This key is used during identity propagation to ensure that the correct subject is instantiated on another application server. This is equally true whether you create a custom subject with custom credentials, or merely add attributes to the standard subject created by WebSphere Application Server.

  • Add information to a subject

    The code below shows how to add custom attributes to a subject. WebSphere Application Server will still create a normal user credential, accessing the user registry as required, but will retain the custom attribute in the subject for future application use during this login session. The custom attribute in the example is added to the public credentials area of the subject (which is probably where you would normally add it). (For this to work in a distributed environment, the SomeType class must be serializable.)

    Listing 3

     
    1. TAIResult negotiateValidateandEstablishTrust

    2. (HTTPServletRequest req, HTTPServletRespose response) {

    3.  
    4. String userid = // get from request somehow

    5.  
    6. SomeType somethingextra = new SomeType();

    7. ... do whatever to somethingextra ...

    8. Subject subject = new Subject();

    9. Subject.getPublicCredentials().add(somethingextra);

    10.  
    11. return TAIResult.create(HTTPServletResponse.SC_OK, userid, subject);

    12. }

  • Create a custom subject with custom credentials

    If you want to completely override the authentication process and complete a full custom subject with all required credentials, a hashtable of authentication data must be created and added to the subject, as in this sample code:

    Listing 4

     
    1. String userid = //get from request

    2. InitialContext ctx = new InitialContext();

    3. UserRegistry reg =(UserRegistry)ctx.lookup("UserRegistry");

    4. String uniqueid = reg.getUniqueUserID(userid);

    5.  
    6. //define groups

    7. ArrayList groups = new ArrayList();

    8. // add admin group

    9. groups.add(reg.getUniqueGroupId("Administrators"));

    10.  
    11. // stash in hashtable

    12. Hashtable hashtable = new Hashtable();

    13. hashtable.put(AttributeNameConstants.WSCREDENTIAL_UNIQUEID,uniqueid);

    14. hashtable.put(AttributeNameConstants.WSCREDENTIAL_SECURITYNAME,userid);

    15. hashtable.put(AttributeNameConstants.WSCREDENTIAL_GROUPS,groups);

    16. hashtable.put(AttributeNameConstants.WSCREDENTIAL_CACHE_KEY,uniqueid+"MyCustom");

    17.  
    18. Subject subject = new Subject();

    19. subject. getPublicCredentials().add(hashtable);

    20. return TAIResult.create(HTTPServletResponse.SC_OK, "ignored", subject);

    In this example, we create our own custom array of groups for the user, containing the single element "Administrators". We add this to the hashtable of information for the user, which you can see we also update with the user ID and unique ID (obtained from the user registry). We also create a custom cache key to ensure that the subject will be recreated correctly on propagation .

    As you can see from the sample above, WebSphere Application Server will expect to find specific key information in the hashtable in the subject. The keys are defined in com.ibm.wsspi.security.token.AttributeNameConstants and they are used as follows:

    • Key: WSCREDENTIAL_UNIQUEID
      Description: This should be a unique representation of the user. The best way to obtain a unique ID for a user is to call the WebSphere Application Server UserRegistry method: public String getUniqueUserId(String userSecurityName). This will also ensure compatibility with the WebSphere Application Server default implementation for the unique ID. If you need to customize the unique ID for some reason, the best way would be to write a Custom User Registry that was able to generate meaningful and appropriately unique IDs.
      Format: this is a java.lang.String.
      Expected format examples (realm/uniqueUserId):
      LDAP: "ldaphost.austin.ibm.com:389/cn=user,o=ibm,c=us"
      Windows: "MYWINHOST/S-1-5-21-963918322-163748893-4247568029-500"
      UNIX: "MYUNIXHOST/32"

    • Key: WSCREDENTIAL_SECURITYNAME
      Description: This is the securityName (commonly called the user ID or short name) of the authenticated user. (WebSphere Application Server uses the securityName attribute for the getRemoteUser(), getUserPrincipal() and getCallerPrincipal() APIs.) The best way to ensure compatibility with the WebSphere Application Server default implementation for the securityName value is to call the WebSphere Application Server UserRegistry method: public String getUserSecurityName(String uniqueUserId).
      Format: this is a java.lang.String.
      Expected format examples:
      LDAP: "user" (ldap UID)
      Windows: "user" (Windows username)
      UNIX: "user" (UNIX username)

    • Key: WSCREDENTIAL_GROUPS
      Description: This is an ArrayList of realm qualified groups to which this user belongs. The format of these groups is important as they are used by the WebSphere Application Server authorization engine for group-to-role mapping in the deployment descriptor. The format provided must match what the WebSphere Application Server default implementation expects. If you use a third party authorization provider, then this should be whatever the third party provider expects. The best way to ensure compatibility with the WebSphere Application Server default implementation for the unique group ID value is to call the WebSphere Application Server UserRegistry method: public List getUniqueGroupIds(String uniqueUserId). If the HashMap is present in the subject created by the TAI but it contains no groups, then WebSphere Application Server will create a credential with the user having no groups; it will not query the registry to obtain group information in this scenario.
      Format: this is a java.util.ArrayList of java.lang.String.
      Expected format examples for each group in the ArrayList:
      LDAP: "ldap1.austin.ibm.com:389/cn=group1,o=ibm,c=us"
      Windows: "MYWINREALM/S-1-5-32-544"
      UNIX: "MY/S-1-5-32-544"

    In addition to the three keys listed above, you may also need to specify a fourth key, the cache key, which is a unique identifier further defining the uniqueness of these credentials. The cache key defines how WebSphere Application Server internally caches subject information. However, it is important to understand that if you let WebSphere Application Server use the default cache key, it will recreate a default subject as needed, which means that customization of the subject could be lost.

    This point is crucial. If you are providing only information that could also be directly derived from the UserRegistry, there is no need to provide the unique cache key. WebSphere Application Server will cache the information provided using the user's unique ID, and if the information must be recreated, then WebSphere Application Server will simply query the registry. However, if the information you are providing is not derivable from the UserRegistry, or is perhaps unique to this particular authentication session, then a unique cache key must be provided. The key must be appropriately unique; that is, it must at least be globally unique across all users. For example, if the subject will always contain the same information for the same user, the user's unique ID plus a simple hard coded constant value is sufficient (as we've done in our example above). On the other hand, if the information in the subject is unique to this specific login session (perhaps it contains the login time or login source), then the cache key must be dynamically generated (again including the user's unique ID) and should be unique across all users and instances of your custom login module or TAI. The cache key definition is:

    • Key: WSCREDENTIAL_CACHE_KEY
      Format: this is a java.lang.String

TAI scenario and example

The following code shows an example TAI that incorporates some of the concepts that we have discussed so far. This TAI accepts three parameters, previously set by an unsecured Web login application: the user ID and password of the requestor, plus a flag called AdminPriv, which enables a user who would normally be given administrator privileges (that is, they are a member of the admin registry group) to either accept or decline those privileges. This is a somewhat unrealistic example, but it serves the purpose of showing how to override registry attributes by creating a custom subject.

The TAI will verify the user's password using the UserRegistry, obtain the user's groups, and also verify that the user is actually a member of the admin group if they request admin privileges. If the user does not request admin access and the user is a member of the admin group, the TAI will remove that group from the group list and pass it to WebSphere Application Server. In this case, a custom key must be created because we have modified the subject based on some dynamic, non-registry driven information (in this case the user's preference to be admin or not for this particular session). In all other cases, the TAI will just use the default group list from the user's registry entry, so a custom cache key is not needed (the subject will contain only default information).

TAI installation and configuration

As discussed earlier, to develop a custom TAI you need to implement the TrustAssociationInterceptor interface defined in the com.ibm.wsspi.security.tai package. The WebSphere Application Server library in which this interface is defined is in the JAR file wssec.jar. The implementing JAR file that contains your custom TAI (we have provided loginexamples.jar for this purpose) should be deployed in the WebSphere Application Server environment in a location that is accessible by the security portions of the application server runtime. Your custom TAI implementation should be deployed under the WAS-INSTALL/lib/ext directory for the application server nodes because the entire WebSphere Application Server runtime can access code here. You may encounter problems if you try to place your TAI under a shared library for just the application server.

Assuming that you have enabled Global Security for your server, follow these steps to make sure you properly configure your custom TAI:

  1. Install your TAI JAR in WAS-INSTALL/lib/ext. For our example, we simply copied the loginexamples.jar file to this directory for our WebSphere Application Server V6 install.

  2. From the WebSphere Application Server administrative console, navigate to Security => Global Security and ensure that the Active authentication mechanism dropdown is set to Lightweight Third Party Authentication (LTPA) (Figure 7).

    Figure 7. Global Security Enabled with LTPA

    Figure 7. Global Security Enabled with LTPA

  3. Verify that LTPA is configured for use on your server by selecting Security => Global security => Authentication mechanisms => LTPA and you should see the Password attribute already filled in.

  4. Select Security => Global security => Authentication mechanisms => LTPA => Trust association and check the Trust Association Enabled box, then Apply .

  5. Select Security => Global security => Authentication mechanisms => LTPA => Trust association => Interceptors . Click on New . Enter the fully qualified classname to your custom TAI class, then Apply . (Figure 8)

    Figure 8. Our example TAI configuration

    Figure 8. Our example TAI configuration

  6. Our sample TAI does not depend on any custom properties. However, if yours does, then select Security => Global Security => Authentication Mechanisms => LTPA => Trust Association => Interceptors => yourTAIclass => Custom Properties => New to enter the (key, value) pairs for the properties on which yourTAI depends, then Apply .

  7. Save your configuration and then restart your server to make your TAI fully operational.

Running the sample TAI

After you complete the TAI configuration and install the TAITestEAR.ear file (which consists of a single WAR file that contains a login JSP and a Servlet called PrintUserInfo), open a browser and navigate to the login.jsp page, as shown in Figure 9.

Figure 9. Example login page

Figure 9. Example login page

Our example behaves as follows:

  • Case 1

    If we log in with a user that has administrative privileges (that is, the user is a member of the admin group in the user registry), and that user checks the box stating that they desire to log in with those privileges, the following code in our TAI is executed:

    Listing 5

     
    1. //determine if user wants to be admin.

    2. String adminPriv = req.getParameter("AdminPriv");

    3. if (adminPriv != null && adminPriv.equals("Y")) {

    4. System.out.println("User desires admin");

    5. wantsAdmin = true;

    6. }

    7.  
    8. //go through groups and remove admin group if needed.

    9. Iterator iter = groups.iterator();

    10. boolean foundAdmin = false;

    11. while (iter.hasNext()) {

    12. String gid = (String) iter.next();

    13. if (gid.equals("admin")) {

    14. foundAdmin = true;

    15. if (wantsAdmin == false) {

    16. iter.remove();

    17. customGroups = true;

    18. }

    19. break;

    20. }

    21. }

    22.  
    23. //Now, a quick error check (wanting admin when not an admin)

    24. if ((!foundAdmin) && wantsAdmin) {

    25. ... Error code not shown ...

    26. }

    27.  
    28. String key;

    29. if (customGroups) {

    30. key = uniqueid + "ExampleTAIAdminRemoved";

    31. } else {

    32. key = uniqueid;

    33. }

    34.  
    35. Subject subject = createSubject(userid, uniqueid,

    36. convertGroupsToUniqueIds(reg, groups), key);

    37. return TAIResult.create(HttpServletResponse.SC_OK, "notused", subject);

    The full source code with all details is available in the included download file , but as you can see, we test whether the user wants to be an administrator and then check to see if they are in the admin group. If the user wants to be an administrator and is in the admin group, we leave the group list untouched.

    In this case, the user's group list is unaltered, since the user is in the admin group and wants to be in that group. After determining the group list, we create a subject and return it to WebSphere Application Server using TAIResult. The resulting output from the PrintUserInfo servlet is shown in Figure 10.

    Figure 10. Admin user logged in with admin privileges

    Figure 10. Admin user logged in with admin privileges

    Figure 10 shows that the user's subject contains the unique group ID customRealm/987, which corresponds to the admin group in our registry. This is essentially the subject as WebSphere Application Server would have created it.

  • Case 2

    Now, let's do this again -- this time customizing the groups -- and see how it is different.

    Close the browser to destroy the user's credential, reopen a new browser and log in again with the same user. If we uncheck the administrative privileges box (indicating this user does not wish to log in with admin privileges), the code previously shown is executed. In this case, however, the wantsAdmin variable is set to false. As a result, the admin group will be removed from the group list. This also means that the customGroups Boolean will be set to true. As a result, we will create a slightly different custom cache key to ensure that the two subjects are distinct.

    The output for this case is shown in Figure 11. Notice that the user still has the admin group defined in the user registry, but we have removed it from the custom subject.

    Figure 11. User's custom subject information with admin group removed

    Figure 11. User's custom subject information with admin group removed

You can observe other use cases by reviewing the sample code in the download file , such as the case where a user who does not belong to the admin group asks for admin privileges. The sample also checks the user's password, even though this is something you might not typically do in a real TAI, as its purpose, after all, is to trust (the T in TAI!) the front end authenticator.

Now, let's look at a really interesting situation regarding our last example. Since we are using a subject that contains custom group information in the scenario with the admin group removed, what would happen if our subject has to be recreated later? We discussed this possibility in the cache key section earlier. To show how important this is, we will create a scenario and see what happens if we had not used our custom cache key. Here is the code we use for creating the subject:

Listing 6

 
  1. private Subject createSubject(String userid, String uniqueid, List groups,

  2. String key) {

  3. Subject subject = new Subject();

  4. Hashtable hashtable = new Hashtable();

  5. hashtable.put(AttributeNameConstants.WSCREDENTIAL_UNIQUEID, uniqueid);

  6. hashtable.put(AttributeNameConstants.WSCREDENTIAL_SECURITYNAME, userid);

  7. hashtable.put(AttributeNameConstants.WSCREDENTIAL_GROUPS, groups);

  8. System.out.println("Subject cache key is " + key);

  9. hashtable.put(AttributeNameConstants.WSCREDENTIAL_CACHE_KEY, key);

  10. subject.getPublicCredentials().add(hashtable);

  11.  
  12. return subject;

  13. }

Go ahead and comment out the line of code out that sets the cache key, rebuild it, and then update the TAI JAR file. Once that is done, follow these steps:

  1. Start two application servers (non-clustered) in the same cell, with both running our sample application. Server1 is listening on port 9443 and server2 is listening on port 9444.

  2. We will run the application's login.jsp from server1 and login with a member of the admin group, leaving the administrative privileges box unchecked. This will execute the section of code that creates the custom subject (by removing the admin group from the user's subject).

  3. This will display the page shown in Figure 11: our admin user with the admin group stripped from the custom subject. This custom subject has been cached on server1, and the LTPA token has been created and sent to the user's browser. 到目前为止,一切都很好。

  4. Now, stop server1 and simply modify the URL in the browser's address window so that the port is changed from server1's 9443 to server2's 9444, then press Enter. This sends the request to the instance of our application that has thus far been idling unused on server2. What we see now is shown in Figure 12. Notice that the admin group has reappeared in the user's subject! In a real-life scenario, this change would likely be much more subtle and difficult to detect.

Figure 12. The subject information has been incorrectly recreated at server2

Figure 12. The subject information has been incorrectly recreated at               server2

Let us review what has happened behind the scenes. We ran this scenario with the powerful WebSphere Application Server trace facility on (we used com.ibm.ws.security.* and com.ibm.websphere.security.*), so we will show a few snippets for better understanding.

  • When the request came in to server2, WebSphere Application Server realized that the URI was protected:

     
    1. [5/11/05 14:41:12:598 EDT] 0000003a WebCollaborat 3 URI - /PrintUserInfo.GET is

    2. protected

  • WebSphere Application Server sees the LTPA cookie in the request header and tries to retrieve the corresponding subject from its own security cache. Since this was in server1, it is not found.

  • Next, WebSphere Application Server tries to use DynaCache to retrieve the subject:

     
    1. [5/11/05 14:41:12:649 EDT] 0000003a AuthCache < getSubject(token)

    2. subject=null Exit

    3. [5/11/05 14:41:12:649 EDT] 0000003a distContextMa >

    4. getOpaqueTokenFromCacheOrOriginatingServer Entry

    5. [5/11/05 14:41:12:649 EDT] 0000003a distContextMa 3 Getting distributed object

    6. from DynaCache.

  • The subject is not found in DynaCache, so WebSphere Application Server next attempts to get the subject from the original server via an MBean:

     
    1. [5/11/05 14:41:12:719 EDT] 0000003a WSCredentialT <

    2. getDistributedObjectNotShared (null) Exit

    3. [5/11/05 14:41:12:719 EDT] 0000003a distContextMa 3 Not found in DynaCache,

    4. getting distributed object using MBean.

  • To determine where the originating server is, WebSphere Application Server decrypts the LTPA token, which has the server name and SOAP admin port:

     
    1. [5/11/05 14:41:12:759 EDT] 0000003a LTPAToken2 3 tokenString after decrypt:

    2. expire:1115844008917$host:billhinest40.hines.ibm.com$java.naming.provider.url:corb

    3. aloc\:iiop\:billhinest40.hines.ibm.com\:9810/WsnAdminNameService$port:8878$process

    4. .serverName:HinesCell01\:Node01\:server1$security.authMechOID:oid\:1.3.18.0.2.30.2

    5. $type:SOAP$u:user\:customRealm/456%1115844008947%fwJYbzUveypeYKWrjjzlZqfhfqHyXwoUa

    6. pknRpoV2FfyMoQcU2hHBGBaU4C648tr40vySBdFJ2SXYN0MFkOWVVS80nywQYiqEE9Bm2JsezPjTDKWWgy

    7. S8vnBpq6YobrNdlf5uTlOFREZHAo8cv4w47fJRNoE9ohZkS26t4lrGog=

  • Of course, the attempt fails, as server1 is now down:

     
    1. [5/11/05 14:41:14:782 EDT] 0000003a WSCredentialT 3 Exception occurred getting

    2. admin client connection.

    3. com.ibm.websphere.management.exception.ConnectorException: ADMC0016E: The

    4. system cannot create a SOAP connector to connect to host

    5. billhinest40.hines.ibm.com at port 8878.

  • WebSphere Application Server will now try to recreate the user's subject using token information:

     
    1. [5/11/05 14:41:14:822 EDT] 0000003a ltpaLoginModu 3 Using credential token for

    2. authentication

    3. [5/11/05 14:41:14:822 EDT] 0000003a ltpaLoginModu 3 Converting SSO token to

    4. authentication token.

    5. [5/11/05 14:41:14:822 EDT] 0000003a LTPAServerObj > validateToken Entry

    6. [5/11/05 14:41:14:822 EDT] 0000003a LTPAServerObj < BEGIN VALIDATING TOKEN: some

    7. errors may occur, look for SUCCESS: Exit

  • Without the presence of the custom cache key, WebSphere Application Server recreates the subject by querying the registry to find the user's ID and group associations. This results in a problem that must be clearly understood: the user's credentials have been changed by accident. The user opted to not have admin privileges when they logged in, and now they have the privileges. This should not be allowed to occur.

This is a trivial example, but as you might imagine there are situations where this kind of behavior could be intolerable.

Let's run the same test again (the user again leaves the administrative privileges box unchecked), but this time with the custom cache key code back in place. It is very important to understand what happens next, as the failover scenario can result in very subtle errors. To illustrate the new chain of events, we will again show the WebSphere Application Server trace flow:

  • As in our previous scenario without the cache key, the attempt to connect to server1 via an MBean fails. But this time, the runtime sees the custom cache key in the LTPA token from the TAI. Since this indicates to WebSphere Application Server that a custom subject was created, WebSphere Application Server should not create the subject on its own; the TAI will have to be called. We can see this in the trace:

     
    1. [5/11/05 18:41:05:015 EDT] 00000035 distContextMa 3 Exception getting opaque

    2. token from originating server.

    3. com.ibm.websphere.security.auth.WSLoginFailedException: SSO token uniqueID not

    4. null, but opaque token not found. Need to re-challenge the user to login again.

    5. [5/11/05 18:41:05:075 EDT] 00000035 TrustAssociat 3 Check if target interceptor

    6. [1]: examples.was.login.ExampleTAI ...

    7. [5/11/05 18:41:05:075 EDT] 00000035 TAIWrapper > isTargetInterceptor()

    8. Entry

    9. [5/11/05 18:41:05:075 EDT] 00000035 SystemOut O isTargetInterceptor

    10. called

  • We now arrive at another very important point: When the TAI is invoked again, we do not have the original context that we had on the initial login, where the user had just filled out the login JSP and its contents were included on the request. Hence, the information (such as the administrative privileges checkbox) is not available. Given that this is essential to our decision tree in building the subject, we must detect this and then inform WebSphere Application Server that we cannot process this request.

  • This information causes WebSphere Application Server to continue with normal authentication (our sample application has a defined login page, so that is what it will be called). The code to handle this situation is below:

     
    1. if (fromLoginJSP == null || !fromLoginJSP.equals("Y")) {

    2. // The TAI has been invoked, but the hidden flag that indicates

    3. // that it is from the login JSP is not in place. This can happen

    4. // in scenarios such as the user being failed over to another

    5. // application server, and this new server being

    6. // unable to retrieve the custom subject via the cache key.

    7. System.out.println("Didn't come from login JSP. Default behavior

    8. will occur.");

    9. return false;

    10. } else {

    11. System.out.println("TAI will handle request.");

    12. return true;

    13. }

  • Notice that we used a hidden field in the login JSP to facilitate this; the TAI checks for the presence of this field to decide whether it is being called as a result of the login JSP being submitted. It would not have been good enough to simply check for the presence of the administrative privileges checkbox; due to the nature of the HTTP protocol, this variable is not included in the request parameters if the box is not checked. The resulting screen is shown in Figure 13, which displays because the application has defined a login page in web.xml. Our TAI had nothing to do with the display of this page.

Figure 13. Redirect back to the login JSP with error

Figure 13. Redirect back to the login JSP with error

In cases where the custom subject is not context-sensitive, the TAI can proceed to recreate the subject. If you are using a front-end proxy authenticator, such as IBM Tivoli Access Manager WebSEAL, this will normally be OK, since all necessary context will have been provided. If the subject is recreated, WebSphere Application Server would then create a new LTPA token with the correct server name and admin port number for the server that re-authenticated the user, in this case server2. You will need to be more cautious about this type of error trapping when using your own TAI if it relies on dynamic or environmental factors to modify the subject.

(If you are wondering why our TAI always creates a subject even when the subject just contains default group information, we do this to work around a problem with subject caching. See the cache issues section for more.)

JAAS usage

JAAS login configurations

JAAS login modules

As described in the authentication plug points section, custom JAAS login modules can be added to the WebSphere Application Server login configurations to modify the subject. Typical uses of such user-written login modules are to assert some user identity to WebSphere Application Server, or to perform custom identity mapping.

For a login module to assert user identity information to WebSphere Application Server, it must execute prior to the IBM ltpaLoginModule (which creates the user credentials) and place a hashtable of user information in one of two places in the subject: either in the sharedState or the public credentials areas, both of which are normally used portions of any JAAS login module sequence. This user identity assertion must be performed in the JAAS login() method.

Here is a small code fragment showing where to place this in the subject -- this being the shared subject that all JAAS login modules have access to:

subject.getPublicCredentials().add(hashtable);

The hashtable shown here is assumed to have been created with the exact same format as was shown earlier in the TAI example. The WebSphere Application Server ltpaLoginModule will see this hashtable when it executes its login() method, but rather than authenticating the user and querying the registry, it will directly create credentials using the information found in the hashtable instead. Note that there can be only one hashtable in the subject, otherwise this will become ambiguous. We therefore prefer to place the hashtable in the sharedState area using a well defined key, as follows:

sharedState.put(AttributeNameConstants.WSCREDENTIAL_PROPERTIES_KEY, hashtable);

If you are unfamiliar with how to obtain the sharedState object or the subject in a JAAS login module, refer to the Java documentation for JAAS. Both are provided as part of the initialize() method to the login module.

Callbacks available to login modules

As mentioned earlier, JAAS login modules can use callbacks to obtain information relevant to the authentication from the environment. WebSphere Application Server supports several callbacks, which are described in detail in the WebSphere Application Server Information Center .

This table summarizes the available callbacks, and details when a callback is available depending on the login configuration being executed.

Table 1. Available callbacks

打回来WEB_INBOUNDRMI_INBOUNDRMI_OUTBOUNDDEFAULT
NameCallback有空有空无法使用有空
PasswordCallback有空有空无法使用有空
WSCredTokenCallbackImpl有空有空无法使用有空
WSTokenHolderCallback有空有空无法使用有空
WSServletRequestCallback有空无法使用无法使用无法使用
WSServletResponseCallback有空无法使用无法使用无法使用
WSAppContextCallback有空无法使用无法使用无法使用
WSProtocolPolicyCallback无法使用无法使用有空无法使用

Login types

It is important to understand that login modules are not only called at initial login time; they are called whenever WebSphere Application Server needs to refresh credentials on an application server. As a result, all callback information will not always be available to the login module. This is because some callbacks provide environmental information that is specific to the particular request invocation. For example, if a login module looks for a particular field in a request object - assuming that the user has just provided it - that same login module might fail in another scenario where the user has not just entered input. There are three distinct login scenarios, described here (and discussed further later):

  • Initial login - The true first login and authentication by a user. At this time, all callback information, including any user input, should be available.

  • Initial login with SSO token - This will normally never occur with custom login modules that have properly created a custom cache key. In a Web-based environment, this type of login can occur when the client hits a different server that cannot access the user's subject (which has been cached by the WebSphere Application Server security runtime). The default behavior at this time will be to re-authenticate, which means calling the TAI if one exists, or otherwise performing the normal WebSphere Application Server authentication process. This is the ordinary login process. However, there is an option available to enable the login to take place without the subject being available, which means that the JAAS login modules will be called. We will discuss this more later.

  • Propagation login - In this scenario, a new server has been accessed and the subject is available, but additional verification is required. More precisely, the tokens that represent the subject are available and the subject is to be recreated from the tokens (we will discuss tokens more shortly). In this case, a typical custom login module will generally do nothing. Only login modules that manipulate custom tokens are likely to require any action be performed. In recognition of that, WebSphere Application Server provides a simple method on the existing WSTokenHolderCallback for determining if this is a propagation login. Custom login modules that do not have any meaningful work to perform can easily check for this case. Here is a brief example:

     
    1. if (callback.requiredLogin()) {

    2. //initial login or initial login w/ SSO case

    3. } else {

    4. //propagation login. No work required

    5. return true.

    6. }

JAAS module vs. TAI

One obvious question might be when to use a TAI versus login modules for asserting identity information. In general, you want to use TAIs for Web requests if at all possible, because of their simplicity. The table below shows the differences between the two:

Table 2. JAAS module vs. TAI

特征Login moduleTAI
IBM proprietaryNo, but requires WebSphere Application Server specific code anyway
Ease of Use更难更轻松
Multi-phase authentication没有
Suppress Web login challenge*没有
Can be used for Web calls
Can be used for RMI calls没有
(Re)called for propagation logins没有

* Before the login modules are even invoked, WebSphere Application Server will have challenged the user for their current authentication information. Obviously, if you are trying to develop a Web-based SSO solution, a TAI is the better approach.

As a basic rule of thumb, it is more appropriate to use TAIs for Web authentication and login modules for other situations.

Proxy login modules

Before you can configure your own login modules, we should explain one potentially confusing item when configuring a custom JAAS login module. JAAS is part of the JRE and by default can only see the JVM lib and ext classpaths. To simplify classloading issues, and to keep your JAAS login modules out of the JVM classpath, IBM provides a proxy login module that uses thread-based classloaders, enabling you to place your own custom login modules in the usual places, such as WebSphere Application Server/lib/ext. Figure 14 shows the configuration of a login module where the proxy login module is specified by checking the box for this option.

Custom JAAS login module examples

Example: Identity assertion login module

Our second example shows how to assert portions of a user's identity to WebSphere Application Server and modify the user's group associations dynamically by using a JAAS login module. Since login modules are more likely to be used with RMI/IIOP, our example uses a simple Java client that contacts an EJB running in WebSphere Application Server. We will not show the EJB client or server code here, since these are irrelevant to the example. Just know that the EJB server code prints out the user subject information just like our earlier servlet example. The EJB implements a single remote method, reverseString(). (The complete client and EJB source code are included in the download file as two additional EAR files: ReverseClientEAR.ear and ReverseEJBEAR.ear.)

Our custom login module is named MyBeforeLTPALoginModule. It is in loginexamples.jar which should be placed in WAS-INSTALL/lib/ext. To assert the user information to WebSphere Application Server before it creates the subject on its own, we will insert our login module before the WebSphere Application Server modules on the RMI_INBOUND JAAS login module stack. Figure 14 shows how to configure the login module using the administrative console. Notice that the proxy checkbox discussed above has been selected.

Figure 14. RMI_INBOUND login module proxy being added

Figure 14. RMI_INBOUND login module proxy being added

Figure 15 shows the pane that is displayed after the module has been added. Notice that the default is to add it at the end of the stack, third in order as shown. We will need to use the highlighted Set Order button to fix this.

Figure 15. MyBeforeLTPALoginModule added to the WEB_INBOUND stack

Figure 15. MyBeforeLTPALoginModule added to the WEB_INBOUND stack

Figure 16 shows the pane that is presented when using the Set Order control to change the order of the login modules. In this figure, we have already selected the radio button for our login module and pressed the Move Up button twice to move it to be at the top of the stack.

Figure 16. The Set Order admin console pane

Figure 16. The Set Order admin console pane

Pressing OK in the Set Order pane presents Figure 17. It may be disconcerting at first to see the new login module still at the bottom of the order, but this is only due to the default alphabetical ordering of modules. Looking at the Module order column confirms that our module is indeed the first in sequence.

Figure 17. The new RMI_INBOUND login module order

Figure 17. The new RMI_INBOUND login module order

Now let's see this login module in action:

  • We start our EJB client using launchClient. The EJB client connects to WebSphere Application Server and looks up our EJB and then calls it. As you might expect, the standard WebSphere Application Server authentication process applies so we will be challenged to provide our user ID and password (not shown). Listing 7 shows the launchClient process. If you want to run this example yourself, you will need to install the provided ReverseEJBEAR.ear application, which contains the components that are used by launchClient.

    Listing 7

     
    1. >launchclient

    2. "c:\home\presentationsPapers\AdvancedAuthPaper\ReverseClientEAR.ear"

    3. -CCBootstrapPort=2810 hello

    4. IBM WebSphere Application Server, Release 6.

    5. J2EE Application Client Tool

    6. Copyright IBM Corp., 1997-2004

    7. WSCL0012I: Processing command line arguments.

    8. WSCL0013I: Initializing the J2EE Application Client Environment.

    9. [6/20/05 9:47:04:923 EDT] 0000000a W UOW=null source=com.ibm.ws.util.ImplFactory

    10. org=IBM prod=WebSphere component=Application Server thread=[P=223911:O=0:CT]

    11. WSVR0072W: Ignoring undeclared override of interface,

    12. com.ibm.websphere.cluster.topography.DescriptionManager, with implementation,

    13. com.ibm.ws.cluster.topography.ClientDescriptionManager

    14. WSCL0035I: Initialization of the J2EE Application Client Environment has completed.

    15. WSCL0014I: Invoking the Application Client class Main

    16. String provided: hello

    17. String in reverse: olleh

  • After the authentication challenge takes place, the RMI_INBOUND login module configuration is called. Our custom login module gets control first. In the initialize() method, the references to the subject and callbackhandler are stored.

    Listing 8

     
    1. public void initialize( Subject subject, CallbackHandler callbackHandler,

    2. Map arg2, Map arg3 ) {

    3.  
    4. System.out.println( "MyBeforeLTPALoginModule initialize()" );

    5. this.subject = subject;

    6. this.callbackHandler = callbackHandler;

    7. }

  • Next, the login() method is called by the WebSphere Application Server security runtime. The code shown below is executed in the login() method. Much of this should look familiar, as it is similar to what we did in the TAI example. One area of code that is new, and of particular interest, is the callbackHandler section (discussed earlier). In this particular context, our login module calls out to the JAAS callback handlers provided by WebSphere Application Server to retrieve the username and password that were entered on the basic authentication screen. Also, notice that we used the code described in the login types section to check for the propagation login scenario using the WSTokenHolderCallback. The string arguments to the username and password callbacks may seem superfluous, but providing empty strings results in an IllegalArgumentException.

    Listing 9

     
    1. public boolean login() throws LoginException {

    2. System.out.println( "MyBeforeLTPALoginModule login()" );

    3.  
    4. Callback callbacks[] = new Callback[ 3 ];

    5. try {

    6. callbacks[ 0 ] = new WSTokenHolderCallback( "" );

    7. callbacks[ 1 ] = new NameCallback( "User:" );

    8. callbacks[ 2 ] = new PasswordCallback( "Password:", false );

    9. callbackHandler.handle( callbacks );

    10. } catch ( Exception e ) {

    11. System.out.println( "Login Module failed: " + e );

    12. e.printStackTrace( System.out );

    13. throw new LoginException( e.getMessage() );

    14. }

    15.  
    16. boolean requiresLogin = ( (WSTokenHolderCallback)callbacks[ 0 ] ).

    17. getRequiresLogin();

    18. if ( requiresLogin ) {

    19. System.out.println( "MyBeforeLTPALoginModule: Need to do stuff for an

    20. initial login" );

    21. String username = ((NameCallback)callbacks[ 1 ]).getName();

    22. String password = new String( ((PasswordCallback)callbacks[ 2 ]).

    23. getPassword() );

    24. ((PasswordCallback)callbacks[ 2 ]).clearPassword();

    25. Hashtable hashtable = new Hashtable();

    26. String uniqueid = null;

    27. try {

    28. InitialContext ctx = new InitialContext();

    29. UserRegistry reg = (UserRegistry)ctx.lookup( "UserRegistry" );

    30. uniqueid = reg.getUniqueUserId( username );

    31. reg.checkPassword( username, password );

    32. } catch ( Exception e1 ) {

    33. System.out.println( "Login Module failed: " + e1 );

    34. e1.printStackTrace( System.out );

    35. throw new LoginException( e1.getMessage() );

    36. }

    37. System.out.println( "uniqueid = " + uniqueid );

    38. hashtable.put( AttributeNameConstants.WSCREDENTIAL_UNIQUEID,

    39. uniqueid );

    40. hashtable.put( AttributeNameConstants.WSCREDENTIAL_SECURITYNAME,

    41. username );

    42.  
    43. // Assert the user as belonging to only this group. This is done only

    44. // as an example of how to manipulate the user's groups dynamically.

    45. // We could also have looked up the user's groups in the registry with

    46. // reg.getUniqueGroupIds() and added this one, or subtracted others

    47. // from that list.

    48. ArrayList groups = new ArrayList();

    49. groups.add( 0, "nomemb" );

    50. hashtable.put( AttributeNameConstants.WSCREDENTIAL_GROUPS, groups );

    51.  
    52. // Set unique cache key to prevent cache problems (losing custom

    53. // info, etc) since the info is always the same for the user, I

    54. // just prefix this with

    55. // the login module name and userid.

    56. // But, if the info was unique to *this login* I'd need a more

    57. // unique value...

    58. System.out.println( "Using new cache key" );

    59. hashtable.put( AttributeNameConstants.WSCREDENTIAL_CACHE_KEY,

    60. uniqueid + "ExampleBeforeLTPALoginModule" );

    61. subject.getPublicCredentials().add( hashtable );

    62. return true;

    63. } else{

    64. System.out.println( "MyBeforeLTPALoginModule: This is a repeat

    65. login, nothing to do." );

    66. return true;

    67. }

    68. }

  • The only other significant code in this module is to return true from the commit() method. Listing 10 shows the edited output from the server side EJB. The EJB itself does nothing of interest in our example other than print out information about the current user. Notice that the user has been placed in to the group we asserted, rather than whatever groups might have been found in the WebSphere Application Server registry.

    Listing 10

     
    1. [6/20/05 10:14:19:503 EDT] 00000057 SystemOut O MyBeforeLTPALoginModule

    2. initialize()

    3. [6/20/05 10:14:19:503 EDT] 00000057 SystemOut O MyBeforeLTPALoginModule login()

    4. [6/20/05 10:14:19:503 EDT] 00000057 SystemOut O MyBeforeLTPALoginModule: Need to do

    5. stuff for an initial login

    6. [6/20/05 10:14:19:503 EDT] 00000057 SystemOut O uniqueid = user:customRealm/3

    7. [6/20/05 10:14:19:503 EDT] 00000057 SystemOut O Using new cache key

    8. [6/20/05 10:14:19:533 EDT] 00000057 SystemOut O MYAfterwsMapLoginModule being

    9. loaded

    10. [6/20/05 10:14:19:533 EDT] 00000057 SystemOut O MYAfterwsMapLoginModule

    11. initialize()

    12. [6/20/05 10:14:19:533 EDT] 00000057 SystemOut O MyAfterWsMapLoginModule login()

    13. [6/20/05 10:14:19:533 EDT] 00000057 SystemOut O MyBeforeLTPALoginModule commit()

    14. [6/20/05 10:14:20:755 EDT] 00000056 SystemOut O In reverseString

    15. [6/20/05 10:14:20:765 EDT] 00000056 SystemOut O The WebSphere Application Server

    16. Subject layer thinks you are keys

    17. [6/20/05 10:14:20:765 EDT] 00000056 SystemOut O Looking into your Subject I see

    18. these groups:

    19. [6/20/05 10:14:20:765 EDT] 00000056 SystemOut O Group ID: group:customRealm/nomemb

    20. [6/20/05 10:14:20:765 EDT] 00000056 SystemOut O looking into Subject for custom

    21. stuff

A more sophisticated login module could have asserted a different, arbitrary user ID and group information. The key is that the login module, because it is asserting the user information to WebSphere Application Server, takes on the responsibility for authentication. In our simple example, we used the WebSphere Application Server registry, but you could easily develop your own much more customized authentication approach.

Example: Subject modification login module

Our next example shows how a login module can be used to alter the subject after it has been created by WebSphere Application Server. To accomplish this, we will place our login module after the wsMapDefaultInboundLoginModule per our earlier discussion. Figure 18 shows the login module being added to the RMI_INBOUND configuration.

Figure 18. Custom login module configuration

Figure 18. Custom login module configuration

Figure 19 shows our custom login module in place with the two standard WebSphere Application Server system modules for RMI_INBOUND. Our login module is placed in the appropriate place to modify the subject after WebSphere Application Server has created it, which is after the wsMapDefaultInboundLoginModule. There is no need to change the order as we did in the last example, since the default places it where it needs to be, at the end of the stack.

Figure 19. Custom login module placement

Figure 19. Custom login module placement

We will use the same session EJB as in the last example, which will result in the same basic authentication process as before:

  • Login again as the same user. When the user is authenticated, the login modules are executed on the RMI_INBOUND configuration. The two standard WebSphere Application Server modules (seen in Figure 19) are executed, and then control reaches our module. The initialize() method shown below, in which we store off the reference to the subject for later use, is executed.

    Listing 11

     
    1. public void initialize( Subject subject, CallbackHandler arg1,

    2. Map sharedState, Map arg3 ) {

    3.  
    4. System.out.println( "MYAfterWsMapLoginModule initialize()" );

    5. this.subject = subject;

    6. }

    Next, the login() method is called:

    Listing 12

     
    1. public boolean login() throws LoginException{

    2. System.out.println( "MyAfterWsMapLoginModule login()" );

    3. return true;

    4. }

  • This would be the point where, in some cases, we might perform authentication of the user, such as in our earlier example, where we assert the user's identity to WebSphere Application Server. In this case, since we are letting WebSphere Application Server create the Subject, authentication is not necessary and we can simply return true. The subject is not fully formed at this point, so we will not attempt to alter it here.

  • The next method to gain control in our login module is commit() and, in our case, this is where the real action happens:

    Listing 13

     
    1. public boolean commit() throws LoginException {

    2. String id = null;

    3. System.out.println( "MyAfterWsMapLoginModule commit() " );

    4. Set stuffset = subject.getPublicCredentials( CustomJAASStuff.class );

    5. if ( stuffset != null && stuffset.isEmpty() ) {

    6. System.out.println( "MyAfterWsMapLoginModule never been called before." );

    7. CustomJAASStuff stuff = new CustomJAASStuff();

    8. Set principals = subject.getPrincipals();

    9. if ( ( principals != null ) && ( !principals.isEmpty() ) ) {

    10. Iterator iter = principals.iterator();

    11. Principal p = (Principal)iter.next();

    12. id = p.getName();

    13. }

    14. StringBuffer id2 = null;

    15. id2 = new StringBuffer( id );

    16. stuff.setWord1( "Caller principal in caps = " + id.toUpperCase() );

    17. stuff.setWord2( "Caller principal in reverse = " +

    18. id2.reverse().toString() );

    19. System.out.println( "MyAfterWsMapLoginModule: adding to subject" );

    20. subject.getPublicCredentials().add( stuff );

    21. } else {

    22. System.out.println( "Found existing CustomJAASStuff. Must be a reinvocation." );

    23. return true;

    24. }

    25. }

  • For this example, we have created a plain old Java object (POJO) class that contains a few string objects (word1, word2) and a date object. First, we ensure the class does not already exist, and then we instantiate one for our use. The code retrieves the principal from the subject, and the user ID is retrieved from that. The two String objects in CustomJAASStuff are populated with the user ID, converted to all capitals and reversed. The custom object is then added to the public credentials of the subject, and our commit() method returns true, which commits our changes to the subject.

  • The following code has been added to the EJB to check for our modifications when the subject is passed to it:

    Listing 14

     
    1. System.out.println("looking into Subject for custom stuff");

    2. Set set = WSSubject.getCallerSubject().getPublicCredentials(

    3. CustomJAASStuff.class);

    4. if ((set != null) && (!set.isEmpty())) {

    5. System.out.println("your subject appears to have a CustomJAASStuff in it");

    6. iter = set.iterator();

    7. int i = 1;

    8. while (iter.hasNext()) {

    9. System.out.print("#" + i);

    10. CustomJAASStuff stuff = (CustomJAASStuff) iter.next();

    11. System.out.print(": And in it I find word1 = "

    12. + stuff.getWord1());

    13. System.out.println(". And the date is "

    14. + stuff.getDate());

    15. i++;

    16. }

  • The results are shown in Listing 15.

    Listing 15

     
    1. [6/20/05 9:47:17:301 EDT] 00000057 SystemOut O MYAfterwsMapLoginModule being loaded

    2. [6/20/05 9:47:17:301 EDT] 00000057 SystemOut O MYAfterwsMapLoginModule initialize()

    3. [6/20/05 9:47:17:301 EDT] 00000057 SystemOut O MyAfterWsMapLoginModule login()

    4. [6/20/05 9:47:17:301 EDT] 00000057 SystemOut O MyAfterWsMapLoginModule commit()

    5. [6/20/05 9:47:17:311 EDT] 00000057 SystemOut O MyAfterWsMapLoginModule never been

    6. called before.

    7. [6/20/05 9:47:17:311 EDT] 00000057 SystemOut O MyAfterWsMapLoginModule: adding to

    8. subject

    9. [6/20/05 9:47:19:013 EDT] 00000056 SystemOut O In reverseString

    10. [6/20/05 9:47:19:033 EDT] 00000056 SystemOut O The WebSphere Application Server

    11. Subject layer thinks you are keys

    12. [6/20/05 9:47:19:033 EDT] 00000056 SystemOut O Looking into your Subject I see

    13. these groups:

    14. [6/20/05 9:47:19:033 EDT] 00000056 SystemOut O Group ID: group:customRealm/2

    15. [6/20/05 9:47:19:033 EDT] 00000056 SystemOut O looking into Subject for custom

    16. stuff

    17. [6/20/05 9:47:19:033 EDT] 00000056 SystemOut O your subject appears to have a

    18. CustomJAASStuff in it

    19. [6/20/05 9:47:19:033 EDT] 00000056 SystemOut O #1: And in it I find word1 = Caller

    20. principal in caps = CUSTOMREALM/KEYS. And the date is Mon Jun 20 09:47:17 EDT 2005

  • Notice that our login module is called in the "initial login" state. After our login module completes, the reverseString EJB method executes and simply prints out the subject, which, you may notice, contains the custom information we placed in it (CustomJAASStuff). Also, the user group here is different from the previous example even though we did authenticate as the same user. That is because rather than assert user or group information in this login module, we let WebSphere Application Server get it from the user registry.

Propagation

总览

Earlier, we discussed requirements for integrating custom logic with the authentication model supported by WebSphere Application Server, making use of both the TAI interface and the ability to add custom JAAS login modules to the supported login configurations. What this custom logic does is add information to the JAAS subject that affects the security attributes of the application; that is, the custom information that was added to the subject may be used within the application to make additional, finer grained, authorization decisions. This can include the use of calculated or dynamic group information that is not stored in any registry.

It is possible for custom JAAS login modules to add information to the subject during a system login. This might include generic Java objects which may or may not be serializable. However, it is important to remember that you are probably not running on a single, standalone server, so there are implications as to exactly what you add to the subject for it work as expected in a distributed environment.

The problem with attempting to recreate custom data is that it is not always possible to do so. In some cases, the custom data generated at the first server may have been dynamically derived. For example, one can derive a location identifier based on the originating browser's IP address, derive a specialized role from a form login attribute, or gather authorization attributes specific to the time-of-day that the login first occurred. These are examples of dynamic authorization attributes that could be lost as soon as a user moves to a second server.

To be useful in a distributed, clustered environment, it must be possible to propagate this custom subject. There are two distinct types of propagation that we will address:

  • Horizontal propagation involves making the subject available to all application servers that are accessed by Web clients.
  • Downstream propagation is the ability to pass custom credentials via the RMI/IIOP layer.

Tokens

For propagation to occur, subject information needs to be transmitted over the network. WebSphere Application Server uses Java serialization for custom objects that have been added to the subject; it is therefore essential to use good serialization practices, as defined by Sun™ , to support version-to-version compatibility. WebSphere Application Server itself does not depend on Java serialization for transmitting its own information. Instead, WebSphere Application Server relies on a token framework, where tokens represent various aspects of a user's identity or the subject. The token types used by WebSphere Application Server are:

  • Authentication token - Represents a user identity (not groups, just base identity) and flows downstream over RMI/IIOP connections; equivalent to the older (pre-WebSphere Application Server V5.1.1) version of the LTPA token.

  • Single sign-on (SSO) token - Used for horizontal propagation of user identity, this token is converted to a cookie and sent back to the browser, and is analogous to the older version of the LTPA cookie. The SSO token is essentially another version of an Authentication token that is suitable for use with a Web browser.

  • Authorization token - A new token type that contains most of the user information from the subject, including all group information.

These tokens represent aspects of the subject. A fourth token, the propagation token, is not user specific, but it represents the thread context for downstream propagation. The propagation token will not be discussed here.

Although not covered in this article, it is possible to develop your own tokens based upon one of these four types, enabling you to control how information is actually propagated from server to server. It is even possible to customize the network representation of the pre-defined WebSphere Application Server tokens. Refer to the WebSphere Application Server Information Center for details.

Horizontal propagation

Horizontal propagation is about the ability of credentials (that is, a subject) created on one server to be made available when the same client accesses another server, without passing through an intermediary server (for example, Web authentication).

As we have seen already, a subject is created when a user signs on to WebSphere Application Server. For Web clients, an SSO token is also created and sent back to the browser. This token does not contain the user identity in full; it contains enough information to uniquely identify - and thus retrieve or recreate - a user's subject. The cache key we mentioned earlier is part of the SSO token. When the subject is created, it is placed in the application server's security cache. It can also, for a clustered environment, be configured such that the tokens representing the subject are stored in DynaCache and replicated. In this scenario, if the user accesses a different application server in the cluster, the set of tokens that make up the subject can be retrieved using the SSO token as a key, and then the custom subject can be recreated.

The SSO token contains a unique ID and timestamp that represents the user, optional custom key information, and JMX admin endpoint information for the application server that created (and cached) the subject. When the user accesses an application server with an SSO token, the following steps are taken to retrieve the subject:

  1. The local security cache is searched for an existing, instantiated subject. If the subject is found in the cache, it is associated with the current thread and then used for security purposes.

  2. If the subject is not found in the local cache, DynaCache is searched for the token set that can be used to recreate the Subject. If the tokens are retrieved from DynaCache, a propagation login is performed.

  3. If the DynaCache lookup is not successful, then the JMX endpoint of the originating server is accessed via JMX and the token set is requested. If the tokens are retrieved via JMX, a propagation login is performed.

Figure 20 illustrates this process.

Figure 20. Authentication state diagram

Figure 20. Authentication state diagram

As you can see from the diagram, there are various paths that can be taken depending on the specific situation. As mentioned earlier, there are three distinct login scenarios: initial login, initial login with SSO token, and propagation login. Some of the subtleties associated with the login and propagation processing will be discussed later.

DynaCache usage

WebSphere Application Server creates a private security cache in DynaCache. Tokens representing the subject are placed in the cache by the application server and replicated to the replication domain. This is typically a single WebSphere Application Server cluster within a single cell, so when a Web client accesses a different cluster or cell, the subject will not be available via DynaCache and the system will fallback to JMX access (described next). When tokens are obtained from DynaCache and used to recreate the subject, this results in a propagation login, which works rather differently from an initial login, as shown in Figure 21.

JMX usage

When the subject information is not available from either the local application server security cache or from DynaCache, the runtime will attempt to retrieve the tokens by making a secure JMX admin call to the server that performed the original initial login. Using tokens retrieved by JMX also results in a propagation login. It is possible that the JMX call will fail, either because the originating server is not currently available, or because the credentials are no longer available in the security cache. In this case, the currently accessed server will perform an initial login with the SSO token. The specific behavior in this situation will be described later.

Be aware that if the JMX call crosses cell boundaries, the two cells must share a common security infrastructure -- the same registry, the same LTPA encryption keys, compatible SSL keys, and so on. It is also necessary that the calling application server's identity (the cell security server ID) has administrative authority on the server it is contacting. Only a process with administrative authority can obtain security tokens from another process.

Propagation login vs. initial login

Figure 21 shows the different flow of control between an initial login and a propagation login. Basically, the difference is that a propagation login merely instantiates a Subject using existing information (the tokens), and does not actually authenticate using a registry. On the other hand, an initial login is expected to create the user subject from the available information (perhaps as little as the SSO token).

Figure 21. Login types

Figure 21. Login types

Downstream propagation

Downstream propagation is very different from horizontal propagation. In downstream propagation, an application server that has access to the complete user's subject is calling another application server. There is no need here for the sharing features of an SSO token or complex horizontal subject propagation.

  • When an application server calls a second server, some information needs to be sent along with the request to continue identifying the user for access to the next resource. The information sent depends upon the J2EE RunAs mode (that is, client, server, or specified). The most common RunAs mode is client, which preserves the originating client identity. When RunAs client is specified in the application's deployment descriptor, the authenticated user subject is set as the invocation subject on the thread ready to be used for any outbound request.

  • As with Web propagation, the subject is converted to tokens and these tokens are propagated to the downstream server.

  • Upon receipt, the tokens are converted back into a subject via a propagation login. This handshaking occurs as part of the CSIv2 context negotiation. The resulting subject is associated with the CSIv2 session.

  • Once a CSIv2 session is established between two parties (including an EJB client), the information in it will remain cached for an extended period. Generally speaking, the session will remain as long as the credentials are still valid. Thus, for example, if an EJB client connects to an application server and remains connected while performing various EJB operations, a custom login module will be called when the client connects and then not again until the client's authentication token expires (two hours by default).

Downstream propagation uses the RMI_INBOUND and RMI_OUTBOUND login configurations. RMI_OUTBOUND is used on the sending server and RMI_INBOUND on the receiving server, as shown earlier. You can customize what is sent by the upstream server by adding your own login modules to the RMI_OUTBOUND login configuration. For example, you might want to perform some kind of identity mapping at this point.

Client differences

A standalone J2EE client can perform a JAAS login and then make method calls on EJBs in an application server. When this occurs, a subject is created on the server. This can be a custom subject created using a custom JAAS login module. However, there are some issues.

First, since there is no SSO token on the client in this scenario, and the custom subject is on the server, not the client, there is no way to perform a propagation login if a client accesses another server (either on failover, or just using another server), and a full initial login will take place at the time the new CSIv2 session is created.

Second, since a full initial login occurs on every access to a new server, there can be problems with repeating the authentication. For example, it is possible that the authentication data is no longer valid, perhaps because the user authentication password is time sensitive.

If a custom Subject is created at the client, it will be propagated to each server instance as part of the initial login, but if it was created on the server by login modules, it must be recreated at the new initial login. Unfortunately, it is not appropriate to store security information at the client since clients are subject to compromise and generally outside the trust domain. As a result, if you are using Java clients with WebSphere Application Server, you need to think carefully about your design if custom subjects are to be used, because it will be very difficult to share them.

Go back and try the Java client example again. Change the client to connect to two application servers. Notice that the custom login module is called the first time each application server is contacted and the custom subject is recreated.

Propagation: The big picture

Figure 22 shows various aspects of propagation. You will see that attributes are propagated horizontally (for Web SSO) using DynaCache, while attributes are propagated downstream (for RMI requests) using RMI. This is because WebSphere Application Server can propagate attributes directly as part of the CSIv2 protocol for RMI/IIOP requests, only the SSO token is provided to other servers by the Web client for Web requests. Notice also that there is a fallback mechanism in the Web layer. In the event that a Web SSO request arrives at an application server that is not part of the DynaCache replication domain (perhaps it is in another cluster or even another cell), a JMX call is made to the originating application server to obtain the custom subject information. Fallback is not needed for RMI propagation since the calling server simply sends the tokens on the request.

Figure 22. Propagation

Figure 22. Propagation

Issues with horizontal propagation

Propagation is an extremely complex issue, with many subtle complexities you need to be aware of. As discussed earlier, there are several places in which you as a developer have the ability to customize how WebSphere Application Server authentication works. You can add a custom TAI or JAAS login module, you can add custom attributes to the subject, or even alter the way in which WebSphere Application Server creates credentials, thus bypassing the normal registry lookups. When you do this, you need to be very conscious of the issues that can occur, particularly with what happens in a multi-server environment, and how propagation and failover work. In this section, we will try to address some of these issues and point out some good practices.

When you perform an initial login, you can customize the subject in either a TAI or a login module. The subject will be created in the application server that you initially access and stored locally in the security cache. It will also be added in token form to a private cache managed by DynaCache, and thus potentially made available to all servers in the same replication domain as the original server. If the subject is a standard WebSphere Application Server subject with no modifications, then the subject can be stored using the default cache key, which is guaranteed to be unique for a given user. However, if you have modified the subject, then you will need to augment the cache key with data that is sufficiently and appropriately unique for the situation. Otherwise, if WebSphere Application Server can't find your cached subject for whatever reason, it will simply recreate a default subject when you hit a different application server.

As far as "appropriate uniqueness" is concerned, this is application specific. If the custom subject is always recreated in the same way, then a simple string including the user's unique ID and some additional string constant is sufficient. However, if two logins for the same user create different subjects (because the content of the Subject varies by, for example, the time of day at login, or location of login device), then the cache key would need to contain sufficient information to ensure different cache keys for different subjects for the same user. For example, the key may contain a hash based on the time, or on the IP address of the login device. The unique cache key information is placed by WebSphere Application Server within the SSO token. Remember that the SSO token is the one and only piece of information that is guaranteed to be shared between application servers when performing Web-based SSO.

When you use a Web browser to access a server other than the one to which you originally authenticated, WebSphere Application Server will attempt to locate the subject as previously described, looking first in the local cache, then in DynaCache, and finally attempting to retrieve the required tokens via a JMX call to the originating server. If the tokens required to recreate the Subject cannot be located by these methods, then things get tricky:

  • If there is no custom cache key, WebSphere Application Server will simply perform an initial login using the SSO token as the user's authentication data. The login modules are responsible for recreating the user's subject given only that SSO token. This is exactly the same as the WebSphere Application Server behavior when propagation is not enabled.

  • If there is a custom cache key, then by default, WebSphere Application Server will attempt to re-authenticate the user. That is, the SSO token will be ignored and a normal "authenticate from scratch" will be performed. That means that TAIs will be invoked if available, the user will be challenged to authenticate if needed, and then an initial login will be performed. This may be frustrating to your users.

  • If there is a custom cache key, but the default behavior has been overridden by setting com.ibm.ws.security.webChallengeIfCustomSubjectNotFound to false for the security subsystem (set via the Global Security Additional properties panel), WebSphere Application Server will attempt to carry on without re-authenticating the user. Instead, an initial login will be performed using only the SSO token. Your custom login modules, if any, will be invoked.

Important version note

The behavior described above with respect to the handling of a failed propagation login with a custom cache was altered with APAR PK00852 which is part of WebSphere Application Server V5.1.1.4 and WebSphere Application Server V6.0.1. You must use these versions of WebSphere Application Server for propagation to behave properly in the error case. With earlier versions, the custom cache key will be ignored and the user's subject will silently change to the default value.

If you override the default behavior by setting the com.ibm.ws.security.webChallengeIfCustomSubjectNotFound property, when the subject is lost and an initial login is performed, all that is available to the login modules at this point is the SSO token (and the cache key which is contained in the SSO token). If this provides sufficient information for the login module to correctly create the subject, then it can continue; otherwise it needs to terminate with an error, which will force the authentication to fail. Note that at this point you are stuck: you cannot login successfully, but WebSphere Application Server will not attempt to re-authenticate. The only way to force re-authentication is to destroy the SSO token by shutting down the browser. You should not allow this situation to occur. If you cannot guarantee that your login module can successfully work with only an SSO token, take the default approach of letting WebSphere Application Server repeat the authentication process. This is why the default behavior is to re-authenticate. Only change this if you are sure your login modules can recover.

Notice how the login modules have more responsibility and opportunity for handling complex login scenarios than does a TAI. This is not accidental: login modules provide added flexibility, but at a price. When you write login modules, you are actively integrating with the WebSphere Application Server security runtime, and need to fully comprehend the complexity involved in doing so. The TAI has been kept intentionally simple, and is generally a preferred customer interface in cases where it can be used.

Cache issues

  • Clearing caches

    The lifetime for cached tokens placed in DynaCache is the same as that of the SSO token. Thus, the cached subject data will expire out of DynaCache when the corresponding SSO token expires.

    The JMX SecurityAdmin clearAuthCache call (see the WebSphere Application Server Information Center for details) that invalidates the security cache entry for a user also invalidates the user's subjects that are cached in DynaCache in addition to the authentication cache. However, the JMX clearAuthCache call does not invalidate the CSIv2 session cache. Therefore, using clearAuthCache will not force the user's subject to be re-computed when the client is using IIOP, unlike with Web clients. Of course, logging in again will result in a new subject being created since the old one is gone.

  • Authentication cache implications

    Be aware that WebSphere Application Server caches subjects in an authentication cache, which is accessible via many different lookup keys. Some example lookup keys include user ID, user ID and password, and LTPA token. When first authenticating a user, WebSphere Application Server will usually first attempt to find a cached Subject for the user (refer to the earlier state diagram), rather than creating a new subject. This improves performance but may yield unexpected results when using custom subjects.

    One key example of this behavior is with EJB client access: If the same user authenticates multiple times with the same user ID and password, the same cached subject will be used, even if the subject is customized on the server side. After the subject is created the first time, your login modules will not be called until the authentication cache expires because the only key WebSphere Application Server has for looking up the potentially cached subject is the user ID and password. You can prevent this behavior by customizing the subject on the client side, just by placing any custom value in it. WebSphere Application Server will then bypass the authentication cache and use the login modules to create the subject.

    Go back and try the Java client again. This time, connect repeatedly to the same application server as the same user. Notice that your custom login module is only called the first time. It won't be called again until the authentication cache expires (10 minutes by default).

    A second example of this behavior was described earlier in the TAI section . If a TAI returns just a user ID in the TAIResult object, WebSphere Application Server will look for a subject matching that user ID. This can result in WebSphere Application Server using a previously created and cached custom subject, rather than a default subject. This is why our TAI always creates a subject. In this case, we would prefer that WebSphere Application Server not cache the custom subject based on the user ID (it should use the custom cache key). This behavior means that if any TAI ever creates a custom subject, then every TAI (and login module) must consistently use custom cache keys. This is WebSphere Application Server internal defect #293814 (a fix is not currently available for any release). If this defect impacts you directly, consider contacting IBM Support and requesting a fix.

Enabling propagation

In WebSphere Application Server V6, propagation is enabled by default, so no further action is necessary. You can of course disable propagation if you like. In WebSphere Application Server V5.1.1, propagation is disabled by default. Propagation settings are controlled in two places:

  • The LTPA SSO configuration panel enables you to configure Web inbound security attribute propagation.
  • The CSIv2 Inbound and Outbound Authentication panels let you configure downstream security attribute propagation.

结论

This article described the advanced authentication features available in WebSphere Application Server to support a more flexible authentication model, including the use of the new, enhanced TAI interface, as well as the custom JAAS login modules. Also discussed were some of the dangers that can trap the unwary developer when dealing with login propagation in a clustered environment.


翻译自: https://www.ibm.com/developerworks/websphere/techjournal/0508_benantar/0508_benantar.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值