IBM WebSphere 开发者技术期刊: WebSphere Application Server V6 中的数据库标识传播

了解如何通过开发能让 J2EE™ 应用程序透明地向数据库发送标识信息的代码来获得主要安全利益。获得 J2EE(包括 CMP Bean)的优势,并仍然利用数据库强大的安全性。

Keys Botzum , 高级 I/T 咨询专家, IBM Software Services for WebSphere

Keys Botzum 是 IBM Software Services for WebSphere 的高级顾问。他有十年大规模分布式系统的设计经验,同时也是一名安全方面的专家。Botzum 先生使用过多种分布式技术,包括 Sun RPC、DCE、CORBA、AFS 和 DFS。最近,他一直专注于 J2EE 及相关技术的研究。他拥有斯坦福大学 (Stanford University) 计算机科学的硕士学位和卡内基梅隆大学 (Carnegie Mellon University) 应用数学与计算机科学的学士学位。Botzum 先生发表过许多关于 WebSphere 和 WebSphere 安全性的论文。Keys Botzum 的其他文章和介绍可以在 http://www.keysbotzum.comIBM developerWorks WebSphere 中找到。他还著有 IBM WebSphere: Deployment and Advanced Configuration



Vinod Jessani (jessani@us.ibm.com), 高级软件工程师, IBM Software Services for WebSphere

Vinod Jessani 是位于圣地亚哥的 IBM WebSphere Enablement Team 的一名高级软件工程师,他在分布式系统和联机事务处理系统方面有十年以上的工作经验。作为 WebSphere Enablement 团队的一名高级成员,Vinod 主管一些售前活动,包括 WebSphere Application Server 和 WebSphere 开发工具的试验、概念证明及技术介绍。他拥有印度浦那大学 (University of Poona) 计算机应用的硕士学位。



Soloman Barghouthi (soloman@us.ibm.com), 顾问兼软件工程师, IBM Rochester Lab

Soloman Barghouthi 是 IBM Rochester Lab 的一名顾问兼软件工程师。他作为 San Francisco 项目开发人员开始在 IBM 的职业生涯,目前是 WebSphere Connection Management 团队的一名开发人员。Soloman 是一名数据库专家,擅长 Oracle,对 DB2 也有很深的了解。Soloman 负责 WebSphere 中的 Cloudscape 和 Oracle 操作。



2005 年 6 月 15 日

引言

恰当地实现强系统安全控制是企业系统的重大挑战之一。简单地说,需要以适当的授权对关键数据加以保护。用 J2EE 应用程序代码联系数据库的多层 J2EE 系统要应对这一挑战的最大困难在于:传统上,最终用户的标识信息没被保存。我们将应用程序运行所代表的用户称为“最后用户”,也就是说,如果在应用程序中使用标准 J2EE 安全性对 Bob 进行身份验证并通过,则 Bob 就是最终用户。

端到端用户标识的丢失意味着会有重大问题,因为许多组织为了保护他们在数据库中的数据,已经精心布置了许多措施。这些措施通常包括数据库审核日志(指明什么人在什么时候做了什么事情),以及对数据本身严格的授权规则。

在典型的基于 J2EE 的系统中,容器维护着一个已通过身份验证的连接池。虽然应用服务器对每个最终用户都进行身份验证(使用几种 J2EE 身份验证机制中的一种),但最终用户的标识信息对该数据库不可用。这是因为所有数据库访问都是使用连接池中的几种公共共享连接中的一种来执行的。由于历史原因,这导致应用程序必须重新使用应用层中的现有数据库级授权和审核功能。这如果完成恰当会是一种浪费,如果完成不好,则很可能不安全。

幸运的是,许多数据库提供了一种方式,这种方式可以向能够更改与现有连接相关联的授权和审核标识的数据库发送提示。但问题是,只能在应用服务器内部使用这种方式。当然,我们可以在每次访问数据库前在应用程序中插入特定于数据库的代码,但这种方法既麻烦,开销又高——并且当容器本身处理数据库访问时,这种方法就不起使用了,因为它是通过 CMP Bean 实现的。然而,随着 IBM® WebSphere® Application Server V6 的发布,我们为这种问题提供了一个解决方案。

WebSphere Application Server V6 对数据库访问基础设施进行了一些更改,使它可以在事务中的连接使用之前插入自定义代码以影响连接。现在,只需要开发很小的一块特定于数据库的代码,J2EE 应用程序就可以透明地向数据库发送标识信息,以供本地数据库授权和审核功能随后利用。现在您可以获得 J2EE(包括 CMP Bean)的好处,并仍然利用数据库强大的安全性。

本文介绍如何使用 WebSphere Application Server V6 来实现这一点。


方法

前面我们已经略为提及基本方法。要成功实现这一点,关键是依靠两个紧密相关的要素:

  • 我们将利用称为重验证的 Java™ 2 Connector Architecture (J2C) 规范功能,这意味着对资源的现有连接可以更改与之相关联的标识。虽然在规范中没有明确规定,但要使重验证有用,要求它是有效的(例如,比打开一个新连接快)。
  • 我们依赖这样的事实:WebSphere Application Server 关系数据库访问是使用 J2C 连接器构建的。事实上,WebSphere Application Server 显式使用称为 RelationalResourceAdapter 的适配器。此适配器符合 J2C,WebSphere Application Server 运行库用它来访问关系数据库。在版本 6 中,对该适配器进行了增强,使其支持重验证。

现在,我们对我们的方法进行更加详细的讨论。

J2C 和重验证

要理解我们的解决方案,首先需要对 J2C 有所了解,它是 J2EE 的一部分,允许通过 J2EE 应用程序访问企业系统。J2C 通过提供对资源的连接池这一概念来实现这样的访问。资源适配器提供特定于资源的函数(例如,如何打开连接,如何通信,如何确定安全信息流向等)。

虽然与这里没有直接的关系,但值得一提的是,IBM IMS™ 和 CICS® J2C 适配器也支持重验证。

有一点可能不明显,那就是 WebSphere Application Server 数据库基础设施实际上是使用 J2C 框架实现的,因此,如果资源适配器支持重验证,它就可以支持对数据库进行重验证。在 WebSphere Application Server V6 中,对用于所有关系数据库访问的 RelationalResourceAdapter 进行了增强,使其支持重验证。因此,如果底层数据库支持对现有连接进行重验证,则 WebSphere Application Server 基础设施就可以利用它。WebSphere Application Server 使得客户机可以提供自定义代码来利用数据库重验证函数(如果可用)。此外,此自定义代码与应用程序代码相隔离,不会影响应用程序编程模型。

DataStoreHelper 和 J2C 重验证

首先必须启用重验证。在 WebSphere Application Server 中,只需要选中数据源属性面板(图 1)中的 Enable database reauthentication 复选框即可启用。

图 1. 数据源属性
图 1. 数据源属性

在解释重验证如何工作之前,首先应该知道,只有启用了容器管理的身份验证/signon(也就是 res-auth J2EE 应用程序部署描述符设置为 Container)才有相应的重验证。当启用容器管理的身份验证时,WebSphere Application Server 使用已配置的主映射模块来为目标 Enterprise Information System (EIS)(例如数据库)创建含有用户 ID 和密码的 JAAS Subject 实例。这样就可以提供自定义的登录模块。

重验证通常只有在连接池达到最大连接数(一个可配置的连接池属性)后才会生效。

当达到最大连接数时,如果有一个新的请求传入连接池来请求建立连接,而其凭证不匹配池中的任何现有连接,则 WebSphere 池管理器会选择其池中的任何连接,将其标记为“needing-reauthentication”,并将该连接返回给 RelationalResourcesAdapter (RRA)。(如果两个连接主题的 privateCredentials 和 publicCredentials 相匹配,则这两个连接的凭证相匹配。这种等效性是通过对主题的私有和公共凭证使用等效方法来确定的。)

然后 RRA 调用 doConnectionSetupPerTransaction() 数据存储助手方法,并传递一个布尔值来表明需要重验证。doConnectionSetupPerTransaction() 是一个 DataStoreHelper 方法,用户必须实现这个方法才能为重验证提供需要的逻辑。这个方法带有以下参数:

  • Subject——包含新连接请求的凭证。
  • User——新连接请求的用户名。
  • Boolean——表明是否需要重验证。
  • 当前 java.sql.Connection 对象。
  • Object——保留,以供将来使用。

如果主题为空,则用户只会得到一个非空值(如果使用的身份验证是应用程序(而非容器),则会发生这种情况)。另外,doConnectionSetupPerTransaction() 数据存储助手方法的调用环境取决于连接的配置方式;如果连接配置为共享(也就是 res-sharing-scope J2EE 应用程序部署描述符设置为 shareable),则 doConnectionSetupPerTransaction 只会在创建第一个连接句柄并且该连接上的任何事务开始之前调用。另一方面,如果 res-sharing-scope 设置为 unshareable,则每个 getConnection 请求都会导致 doConnectionSetupPerTransaction() 方法被调用(当然,也在该连接的任何事务开始之前)。

结合在一起

有了这些关键的构建块,我们来看一下如何将它们结合在一起以实现我们的目标。

我们的整体方法基本如下:

  1. 创建一个自定义登录模块来向 Subject 添加自定义用户信息。
  2. 创建一个自定义的登录配置,它利用我们的登录模块和 IBM 提供的缺省数据库身份验证登录模块。我们这样做是因为我们还想利用 WebSphere Application Server 数据库身份验证基础设施。
  3. 创建一个自定义 DataStoreHelper,它具有在需要时可以修改数据库连接标识信息的方法。
  4. 配置 DataSource 来使用该自定义的 DataStoreHelper。
  5. 在应用程序安装过程中配置应用程序资源引用以使用该 DataSource 以及我们的自定义登录配置。

一旦以上步骤都完成:

  • 只要有一块应用程序代码(或 WebSphere Application Server CMP 引擎)执行 getConnection() 调用时没有指定用户 ID 和密码,WebSphere Application Server J2C 就会被激活。
  • 然后 J2C 引擎执行我们的自定义登录配置,这个配置先执行 IBM 缺省身份验证登录模块,再执行我们的自定义登录模块。
  • 我们的自定义登录模块只是修改 Subject,使之包含当前用户标识。
  • 然后 J2C 引擎查找与该 Subject 相关联的连接。如果找到,则使用它。如果没有找到,则选择一个现有的连接并进行重验证。(细心的读者可能发现这并不是最佳选择。在 WebSphere Application Server V6 PTF 中,将会对算法进行增强,使之只有在池满时才对现有的连接进行重验证;如果池尚未满,则会使其增长。)
  • 在重验证过程中,我们的自定义 DataStoreHelper 更改连接。
  • 然后 J2C 将此连接返回给调用者,并更新连接池以反映与该连接相关联的新的 Subject。

身份验证别名和缺省主映射
在这些步骤中只有一个小小的“窍门”。IBM 提供的缺省主映射登录模块假设资源引用有一个相关联的 J2C 身份验证别名。遗憾的是,在配置资源引用以使用自定义身份验证时,不能在管理用户界面指定别名。相反,是将别名信息作为资源引用的一个属性来指定。此属性是 com.ibm.mapping.authDataAlias。然后,当在调用自定义模块之前调用缺省的主映射模块时,资源引用会发现它正在寻找的这个属性,接着按正常方式使用 WebSphere Application Server J2C 别名来建立数据库身份验证信息(用户 ID 和密码)。我们将在后面介绍如何对其进行配置。


数据库支持的功能

我们已经通过利用特定于数据库的本地函数,采用 DB2® 和 Oracle® 实现了上述方法。我们在此对其进行一个简要的概括。详细信息应该参考您的数据库文档(请参阅参考资料)。

实质上,要支持本文描述的方法,您的数据库必须:

  • 支持更改与现有数据库连接相关联的授权和审核标识。
  • 能有效地更改连接标识,因为它会频繁出现。

既然连接标识会被更改,数据库就可能需要使用特定功能或组件。对于 DB2,没有特别的需求。对于 Oracle,我们将使用的方法要求使用 Oracle Virtual Private Database 功能;将使用 VPD 定义者为 Oracle 提供的特定 SQL 代码来筛选从数据库返回的信息。我们将在后面介绍这样的例子。

对于 DB2,我们利用最近引入的 Java API resetConnection()。请注意,我们使用的此 API 版本只在 DB2 V8.2 FP 9 和更高版本中可用。我们还需要 DB2 APAR IY71420。此示例在早期版本的 DB2 中无法工作

对于 Oracle,我们遵循 Leveraging Oracle Database Security with J2EE Container Managed Persistence 中的建议(请参阅参考资料),使用 OracleConnection.setClientIdentifier() 方法,它对现有的连接设置审核和授权信息。

虽然我们未检查其他数据库,但此方法不是特定于数据库的。如果您的数据库支持我们需要的关键概念,则可能可以实现一些类似的功能。


实际实现信息

知道基本事项和 WebSphere Application Server 如何提供需要的插入点后,您应该能够自己实现此解决方案。然而,为了使事情简化,我们将以源代码形式提供针对 DB2 和 Oracle 的示例解决方案。

这里提供的示例代码仅仅是:一个示例。我们不保证它在生产环境中的适用性。您应该与您的数据库管理员和其他专家一起创建适合您的环境的一组适当的帮助程序。当然,这些帮助程序应该经过严格的性能和错误测试才能部署到生产中。

我们的示例解决方案实现了一个用于 DB2 和 Oracle 的自定义 DataStoreHelper,以及一个处理 J2C 标识信息的公共登录模块。在实现时,我们发现 DataStoreHelper 的许多控制逻辑对所有数据库来说是通用的,所以我们进行一些重构以共享公共代码。结果如图 2 中的图表所示。

请注意,有两个特定于数据库的 DataStoreHelper:AssertionOracleDataStoreHelper 和 AssertionDB2DataStoreHelper。这两者分别继承于 WebSphere Application Server 帮助程序 OracleDataStoreHelper 和 DB2DataStoreHelper。然而,它们都使用公共的 AssertionGenericHelper 类,这个类包含的控制逻辑大多与数据库无关。实现特定操作的数据库特定代码位于数据库的特定帮助程序中。

要支持重验证和我们这里定义的标识投影模型,每个数据库都必须支持三个关键操作:

  • getUserOnConnection()——返回与连接相关的当前用户。它不是一个关键的方法,只是用作调试目的。
  • setUserOnConnection()——将连接标识设置为特定用户。它假设我们同时设置审核和授权信息。
  • clearUserOnConnection()——通过删除任何标识信息来将连接返回到缺省状态。这意味着连接将恢复到根据连接池标识(您配置的公共用户 ID 和密码)执行审核和授权。当连接被匿名用户从池中取出时将使用此方法。

为了真正向 WebSphere Application Server J2C 运行库提供用户标识信息,我们实现了一个名为 AssertionLoginModule 的登录模块并将它配置到登录配置中,我们稍候将对它进行介绍。此登录模块确定用户的当前 J2EE 标识,然后将其作为特殊主体添加到 Subject 中。这一步很重要,因为它实现两件事:

  • 帮助程序得到用户标识。
  • 更重要的是,通过更改该 Subject,我们通知 WebSphere Application Server J2C 运行库此连接现在与一个特定用户相关联。WebSphere Application Server J2C 缓存将使用该信息来提高性能(通过尝试使用具有相同用户的连接),以及在需要重验证时(与连接相关联的 Subject 需要更改的任何时候)通知我们的帮助程序。

最后,与任何编写良好的 WebSphere Application Server 代码块一样,我们的代码是完全可跟踪的,它使用 WebSphere Application Server JRAS 跟踪基础设施(可以使用标准的 WebSphere Application Server 管理工具来控制跟踪)。

图 2. 类关系图
图 2. 类关系图

以下部分将更加详细地介绍每个类的实现。有关更多详细内容,请参考包含在下载文件中的源代码。

AssertionLoginModule

该登录模块相当简单。与预期的一样,它实现了标准的 Java LoginModule 接口,但任何实际工作都是由执行方法来完成的。当运行库调用 commit() 方法时,该登录模块获取当前用户标识并将它填充到 PrincipalInformation 对象中。然后,与任何编写良好的登录模块一样,它会进行检查以确保 Subject 尚未获得此 PrincipalInformation 对象;然而,如果已经获得该对象,则会删除类型相同的现有 Principal,然后添加此对象。这是很重要的,因为帮助程序假设只有一个类型为 PrincipalInformation 的 Principal。

需要指出的一点是,当前用户的实际用户 ID 是由静态方法 computeUserBasedOnCurrentUser() 确定的。这样做是因为每个帮助程序也执行此相同的计算。如果由于某种原因,系统需要进行一个无关紧要的映射:将 J2EE 用户 ID 映射为数据库用户 ID,则通过更改此方法可以满足这样的需求。

AssertionWithPasswordLoginModule
此模块只是 AssertionLoginModule 的一个子类,一个关键区别在于:它包含 computeUserBasedOnCurrentUser() 方法的一个新的实现,此方法用一个用户 ID 和密码来填充 PrincipalInformation。这样做是因为有些数据库(例如 DB2)要执行重验证需要新目标用户提供一个密码。此密码管理的实现并不重要;我们仅仅在 AssertionWithPasswordLoginModule.computeUserBasedOnCurrentUser() 中包含一个清楚可见的硬编码密码。我们在示例中选择这种明显不切实际的方法是因为任何好的方法都与环境有着密切关系。因此,如果您想设计一个在您的环境中有意义的方法,可以有以下选择:

  • 如果 WebSphere Application Server 直接对用户进行身份验证,则可以在自定义 TAI 或登录模块中捕获该用户的密码并将它放在该用户的 Subject 中。当然,这要求您当时启用 Subject 传播。
  • 如果需要,可以在同一个共享属性文件中查找每个用户的密码。
  • 可以通过标识映射产品(例如 IBM Tivoli® Access Manager)获取用户密码。
  • 可以配置数据库,使所有用户的密码相同,从而消除任何密码管理问题。当然,这样您将必须保证没有用户直接通过网络访问该数据库。为了简单,我们在这里采用这种方法。

以后要运行示例,您可能需要将硬编码的密码计算方式更改为更适合您的环境的方法。(这适用于 DB2。)

AssertionGenericHelper

当您查看数据库特定的帮助程序的实现时,您将注意到它们的所有控制逻辑都遵从 AssertionGenericHelper 类。基本上,IBM 定义的 DataStoreHelper 接口的这些实现都只是调用这个类。AssertionGenericHelper 提供两个 WebSphere Application Server 定义的方法的实现,它们支持在重验证上下文中的连接设置和管理:

  • doConnectionSetupPerTransaction
    在一个事务第一次使用连接之前,由 WebSphere Application Server 运行库调用此方法,它为我们提供一个很好的机会,即可以在连接使用之前对其属性进行更改。在我们的方法中,如果需要重验证,则我们使用数据库特定的代码来更改现有连接上的标识。WebSphere Application Server 通过将布尔值 reauth 标志设置为 true 来告诉我们什么时候需要重验证(意味着与该连接相关联的新旧 Subject 不匹配)。

    如果决定执行重验证,则通过使用 Subject 中的信息(请记住,这是在前面的登录模块中设置的)来计算合适的目标用户 ID。我们也支持如果新的目标用户 ID 为空(意味着未经过身份验证),则清除该连接上的现有用户 ID。

    然而,在我们的方法中,这远远不够。当一个连接首次被使用时,我们仍然需要设置身份验证信息,但 WebSphere Application Server 不会将这视为重验证,因为连接标识没有在第一位置设置。因此,我们必须在下面的方法——doConnectionSetup() 中单独处理这种情况。

  • doConnectionSetup
    只要一个连接第一次被创建并使用(不是在一个事务中,但一定是在它第一次使用时)时,就会调用此方法。这里,我们必须将连接标识设置为当前用户的连接标识(这里没有重验证标志可以帮助我们)。但遗憾的是,由于在本例中没有 Subject,我们无法直接得知我们的登录模块指定了什么用户标识。因此,我们必须做出这样的假设:假设 doConnectionSetup() 是从用户线程的同一线程调用的。事实也确实如此,但这个假设在以后的 WebSphere Application Server 版本中会进行更改(虽然这样的更改不太可能,因为数据库本身依赖此行为)。

    我们可以使用与登录模块相同的方法来获取当前用户的用户 ID,也可以使用 getPrincipalFromThread() 方法,以一般的方式获取它,这个方法是每个帮助程序都必须实现的。一旦确定了标识,我们就可以像使用前面的方法那样,更改连接标识。

DB2AssertionDataStoreHelper

这个类自然就使用来自 AssertionGenericHelper 的控制逻辑,然后实现前面讨论的三个必需的方法。与这个类相关联的方法是:

  • setUserOnConnection
    对于 DB2,我们使用 DB2 JDBC API resetDB2Connection()。通过提供新的用户 ID 和密码,DB2 运行库更改了此新用户将要使用的现有连接。正如期待的那样,此更改的连接的审核和授权(实际上是整个安全环境)都已更新以反映该新用户(有关数据库的详细信息和最佳实践,请参阅参考资料中的 DB2 文档。)
  • getUserOnConnection
    此方法无法在 DB2 中实现,因为没有 API 可以确定与该连接相关联的用户。此方法只用于调试目的,因此并不重要。
  • clearUserOnConnection
    对于 DB2,我们使用 resetDB2Connection() 来将连接返回到其原始状态。要使其生效,我们需要(在别名中定义的)连接池标识和密码值,这些值可以从 Subject PasswordCredential 获得。
  • PrincipalInformation getPrincipalFromThread()
    此方法只是将请求转发给 ssertionWithPasswordLoginModule 以确定正在访问数据库的当前用户。

OracleAssertionDataStoreHelper

这个类也利用来自 AssertionGenericHelper 的控制逻辑,然后实现前面讨论的三个必需的方法。对于 DB2,与此类相关联的方法有:

  • setUserOnConnection
    对于 Oracle,我们在 Oracle 连接上使用 Oracle 定义的 setClientIdentifier() 方法来更改审核和授权信息(详细信息,请参阅参考资料中的 Leveraging Oracle Database Security with J2EE Container Managed Persistence 和 Oracle 文档。)请注意,小写的用户 ID 被转换成大写的,因为 Oracle 用户 ID 始终是大写的,而真正随虚拟的私有数据库实现的架构授权实现是区分大小写的。我们必须确保我们的用户 ID 都是大写的以防止出现这样的小问题。(有关数据库的详细信息和最佳实践,请参阅参考资料中的 Oracle 文档。)
  • getUserOnConnection
    对于 Oracle,我们使用 getUserOnConnection() 方法来确定当前用户。此方法只用于调试目的。
  • clearUserOnConnection
    我们使用 setClientIdentifier(),以一个空的用户 ID 来清除连接中的用户。
  • PrincipalInformation getPrincipalFromThread()
    此方法只是将请求转发给 AssertionLoginModule 以确定正在访问数据库的当前用户。

配置步骤

在这一部分中,我们分步介绍配置一个应用程序的过程,这个应用程序包含一个资源引用以使用带有标识传播的 DB2。(DB2 示例应用程序 TestAuthorizingDSApp.ear 包含在下载文件中。其中也包含相对应的 Oracle 示例应用程序 TestAuthOraDSApp.ear。对于 Oracle 和 CMP Bean,可以使用类似的过程步骤,但这里不再介绍。)以下步骤采用的是下面将要讨论的 DB2 示例应用程序,但因为这些步骤并不专属于任何特定的应用程序,所以我们认为描述这些步骤时不将它们与特定应用程序相关联是有好处的。实际上,除了应用程序安装步骤之外,这里的所有其他任务都可以每单元执行一次(或者每 DataSource 执行一次),而不是每个应用程序执行一次。

  1. 让 WebSphere Application Server 访问代码
    我们开发的代码是由 WebSphere Application Server 运行库使用的,所以必须放在一个在访问数据库资源时可用的类加载器中。我们决定创建一个共享库,并通过一个应用服务器类加载器来使用它:
    1. 获得二进制的 JAR 文件 DBIdAssertionHelpers.jar(或者通过源文件编译),并将它放在计算机中的某个位置。在我们的示例中,我们选择 c:\temp\DBLibraries,但在生产中当然应该选择一个更好的位置。
    2. 在 WebSphere Application Server 中,通过选择 Environment => Shared Libraries 来创建共享库,并为该 JAR 文件指定文件系统位置和库名称。我们的共享库名称为 DBIdentityAssertion(图 3)。单击 OK
      图 3. 创建共享库
      图 3. 创建共享库
    3. 在使用这个库的应用服务器上创建一个新的类加载器。导航至 Application Servers => <your server name> => Java and Process Management => Class Loader => New。接受缺省值并单击 Apply
    4. 选择 Libraries 属性,并单击 Add 按钮对它进行配置,使它使用我们刚创建的库,然后单击 OK(图 4)。
      图 4. 创建新的类加载器
      图 4. 创建新的类加载器

    现在,我们有了一个新的应用服务器级的类加载器,它加载的自定义库包含这里使用的基础设施代码。

  2. 创建身份验证别名
    要对数据库进行身份验证,我们需要一个 J2C 身份验证别名。由于这只是一个例子,所以我们使用 db2admin 来作为我们的用户 ID。(显然,在生产中不应这样做,WebSphere Application Server 不需要数据库管理权限。)
    1. 导航至 Global Security => JAAS Configuration => J2C Authentication Data
    2. 以适当的用户 ID 和密码创建一个新的别名 db2admin。在图 5 中,请注意 WebSphere Application Server 生成的完全限定的别名。后面将需要这个完全限定名称;系统中的实际名称可能有所不同。
      图 5. 创建身份验证别名
      图 5. 创建身份验证别名
  3. 创建 DataSource
    以正常的方式创建一个新的 JDBC 提供程序(选择 DB2 Universal JDBC Driver Provider)然后创建一个 DataSource。在创建好 DataSource 之后,需要对它进行一些更改,以使它支持重验证功能。下面将描述这些实现步骤,其中我们更改了一个名为“DB2 Universal JDBC Driver DataSource”的 DataSource(您的名称可能会有不同)。
    1. 要在该 DataSource 上设置一个用户定义的自定义数据存储助手类,请在 WebSphere Application Server 管理控制台中导航至 JDBC providers => DB2 Universal JDBC Driver Provider => Data sources => <your datasource>
    2. 我们将覆盖缺省帮助程序并指定自己的帮助程序 com.ibm.swservices.security.database.AssertionDB2DataStoreHelper(图 6)。您将注意到示例代码中还有一个自定义的 DataStoreHelper — AssertionOracleDataStoreHelper,它可用于 Oracle。单击 Apply
      图 6. 创建 DataSource
      图 6. 创建 DataSource
    3. 为了使测试更加简单,请将连接池的最大大小设置为 2(在连接池属性中设置)。这将使重验证进行得更频繁,以便观察该行为。
    4. 选择 WebSphere Application Server 数据源属性并选中 Enable database reauthentication(图 7)以在该新数据源中启用重验证,然后选择 OK
      图 7. 启用重验证
      图 7. 启用重验证
    5. 设置缺省架构。虽然这与重验证没有直接的关系,但我们的示例使用不带架构限定符的 SQL 语句来查询数据库。因此,将使用缺省架构值。如果什么都没指定,则使用对数据库进行身份验证的用户的架构。很显然,如果该用户更改了,或者连接池用户(在我们的示例中为 db2admin)的架构与示例所使用的架构不相同,则会出现问题。这里,ADMIN 是包含示例表的架构的名称。您的环境可能会有不同。

      要设置 WebSphere Application Server 在查询数据库时将使用的缺省架构,请选择 DataSource 页面中的 Custom properties 选项来对您的 Datasource 进行设置。选择 currentSchema 属性(图 8,只针对 DB2),在这里指定包含示例表的架构的名称。单击 OK

      图 8. 设置缺省架构
      图 8. 设置缺省架构

    现在,您已更改了 DataSource,使其使用新的自定义 DataStoreHelper 并支持重验证。

  4. 创建自定义登录配置
    我们需要创建一个自定义登录配置来支持 J2C 标识映射功能。对于此配置,需要利用现有的 WebSphere Application Server 缺省标识映射模块,它提供数据库身份验证数据(使用 J2C 别名)。因此,我们的新配置将包括:
    • 我们的缺省模块: com.ibm.ws.security.auth.j2c.WSPrincipalMappingLoginModule
    • 我们的自定义模块: com.ibm.swservices.security.database.AssertionWithPasswordLoginModule,它声明用户标识。(Oracle 用户可以使用 AssertionLoginModule 作为自定义模块。此模块不提供用户密码,但由于 Oracle 支持没有密码的重验证,所以不需要密码。)

    以上模块是在 Application Logins 面板下配置的,但缺省标识的登录模块类名称也可以通过检查 DefaultPrincipalMapping 登录配置来确定。要创建新配置,请执行以下操作:

    1. 导航至 Global Security => JAAS Configuration => Application Logins ,选择 New
    2. 为配置分配一个适当的名称(我们使用 DBAssertion),然后单击 OK
    3. 选择新配置,将它添加到上面列出的缺省和自定义登录模块。在指定类名称时,始终选择 use login module proxy。图 9 显示了在对两个登录模块进行配置之后新的 DBAssertion 登录配置。
      图 9. 配置摘要
      图 9. 配置摘要

    现在,最好将一切设置都保存到存储库中。通常,每个 DataSource 都需要执行一次以上步骤(除了登录配置,它可以被共享)。现在,我们来描述一下每次安装应用程序时需要的步骤。

  5. 应用程序安装
    既然具备所有必需信息的 DataSource 和登录配置已经创建,现在就可以安装应用程序并将它配置为使用这些新配置。其中主要步骤是为 EJB 组件配置资源引用。(对于 CMP Bean,可以通过指定缺省连接工厂(管理控制台中应用程序安装的第 5 步)或 CMP 特定连接工厂(应用程序安装的第 6 步)来实行同样的配置,但这里不对这些变量进行介绍。)

    请注意,示例应用程序不包含 CMP Bean。只有映射 CMP Bean 资源引用,这些示例才会工作。您应该在测试简单一些的情况之后再尝试对这进行测试。虽然这里显示的屏幕截图反映的是我们的示例应用程序,但基本步骤适用于任何应用程序。

    1. 按正常方式安装应用程序(通常只是接受缺省值,但可能要指定目标服务器),直到进入安装向导中的第 8 步:将资源引用映射为资源。
    2. 在页面底部为您的 EJB 选择资源引用,然后滚动到顶部,选择现有的 JNDI 名称(在我们的示例中为 jdbc/DB2AuthDS)。单击该面板上的 Apply。(请参见图 10 和图 11。)
      图 10. 资源引用 JNDI 名称
      图 10. 资源引用 JNDI 名称
      图 11. 将资源引用映射为资源
      图 11. 将资源引用映射为资源
    3. 保持资源引用的选定状态。在面板的“Specify authentication method”部分,选择 Use custom login configuration 和您前面创建的 DBAssertion 登录配置,然后单击 Apply
      图 12. 指定身份验证方法
      图 12. 指定身份验证方法
    4. 在资源引用中添加一个自定义属性。此属性将引用前面创建的身份验证别名,并由 WebSphere Application Server 缺省主映射模块使用。为每个引用单击 Mapping Properties 按钮。您将看到如图 13 所示的画面。
      图 13. 自定义属性
      图 13. 自定义属性
    5. 选择 New 来创建一个新的属性,然后指定以下值:
      • 名称:com.ibm.mapping.authDataAlias
      • 值:<别名名称>
      在我们的示例中,别名是 keysbotzumNode01/db2admin。单击 OK
      图 14. 创建新的自定义属性
      图 14. 创建新的自定义属性
    6. 资源引用页面的右下角显示总结区(图 15)。请注意显示的容器授权(这实际上应该称为“身份验证”),并注意方法 DBAssertion 和我们的自定义映射属性显示。
      图 15. 登录配置
      图 15. 登录配置

现在,我们已经完成了示例应用程序的特定于数据库标识投影的步骤。单击 Next,直到安装完成,这样就完成了应用程序的安装过程,然后保存您所做的更改。

使应用程序安装简单化

为了减轻这些应用程序安装步骤中的繁琐过程和潜在错误,以下提供了一个示例的 wsadmin 脚本,它将安装示例应用程序,并为我们的 EJB 模块中的嵌入式资源引用和 CMP Bean 的缺省资源(上面没有显示)指定需要的资源引用信息。使用该脚本的前提条件是已经定义了 DataSource、登录配置和身份验证别名。您可能需要修改一些不适合的元素才能使它们适用于您的环境。

$AdminApp install C:/home/workWithDev/datasource-
reauth/TestAuthorizingDSApp.ear {-MapResRefToEJB 
{{TestDSEJB TestAuthDSTasks TestAuthDSEJB.jar,META-
INF/ejb-jar.xml jdbc/DB2AuthDS javax.sql.DataSource 
jdbc/DB2AuthDS DBAssertion 
WebSphere:name=com.ibm.mapping.authDataAlias,value=key
sbotzumNode01/db2admin }} -DataSourceFor20EJBModules 
{{TestDB2EJB TestDB2EJB.jar,META-INF/ejb-jar.xml 
jdbc/DB2AuthDS cmpBinding.container DBAssertion 
  WebSphere:name=com.ibm.mapping.authDataAlias,value=key
sbotzumNode01/db2admin}} -MapModulesToServers { 
{TestDB2EJB TestDB2EJB.jar,META-INF/ejb-jar.xml 
WebSphere:cell=keysBotzumCell,node= 
keysbotzumNode01,server=myServer} {TestDSEJB 
TestAuthDSEJB.jar,META-INF/ejb-jar.xml WebSphere:cell= 
keysBotzumCell,node= keysbotzumNode01,server=myServer} 
{TestAuthDSWeb TestAuthDSWeb.war,WEB-INF/web.xml 
WebSphere:cell= keysBotzumCell,node= 
keysbotzumNode01,server=myServer}}}
$AdminConfig save

以上脚本是使用 wsadmin installInteractive 安装过程生成的。当以交互模式完成一个安装时,将会向当前概要中的 wsadmin.traceout 文件发送等效的非交互式脚本行(如上所示)。您自己可以使用 installInteractive 命令来为您的环境生成适当的脚本。

第二种选择是在资源本身中指定容器管理的身份验证:与在每个引用上指定资源身份验证信息不同,您可以在 DataSource 级指定自定义身份验证模块和受容器管理的身份验证别名(JDBC Providers => 您的提供程序 => DataSources => 您的数据源页面),但要注意,WebSphere Application Server V6 中不支持这种做法。图 16 显示如何直接在一个 DataSource 上配置此信息。

图 16. 配置容器管理的身份验证
图 16. 配置容器管理的身份验证

运行示例应用程序

在可以运行示例应用程序之前,需要准备数据库。

配置数据库

在这一部分中我们只总结配置 DB2 或 Oracle 数据库的步骤;我们执行的是缺省安装,它包含随产品附带的示例数据库。

我们所采用的是一个测试系统。您的 DBA 可能会反对在重要的 DB 服务器上这样做。如果是这样,请与您的 DBA 一起讨论如何收集需要的审核信息。

DB2 配置
除了创建必需的用户帐户和配置示例数据库(大多数 DB2 安装缺省情况下都会提供示例数据库)外,不再需要其他的 DB2 配置步骤。然而,如果您有兴趣查看 DB2 审核信息,您将需要启用审核。您的数据库管理员应该能够帮助您实现这一点,不过这里总结了我们在测试服务器上采用的步骤:

  1. 启用审核可以有以下命令:
    db2audit configure scope all
    db2audit start
    db2audit prune all——删除以前的/旧的信息
    db2audit flush——将记录刷新到文件 (db2audit.log)
    del db2audit.out——删除文件
  2. 要获取自最后一次刷新以来所产生的审核信息,可以使用以下命令:
    db2audit extract

    这将会把审核信息以用户可读的形式发送到 <DB2 install>/DB2/security 目录中的 db2audit.out 文件。这里是一些有趣的片段,它们摘自运行测试应用程序所产生的 DB2 审核跟踪:

    以 DB2ADMIN 身份获取原始连接:

    timestamp=2004-12-21-13.41.13.852000;category=CONTEXT;audit
    event=CONNECT;
      event correlator=2;
      database=sample;userid=db2admin;authid=DB2ADMIN;
      origin node=0;coordinator node=0;
      application id=NF000001.LC12.041221184113;application 
    name=db2jcc_application;
    ...

    将连接从 DB2ADMIN 更改到另一个用户:

    timestamp=2004-12-21-13.41.15.354000;category=CONTEXT;audit 
    event=CONNECT_RESET;
      event correlator=3;
      database=sample;userid=db2admin;authid=DB2ADMIN;
      origin node=0;coordinator node=0;
      application id=NF000001.LC12.041221184113;application 
    name=db2jcc_application;
      
    timestamp=2004-12-21-13.41.15.654000;category=CONTEXT;audit 
    event=CONNECT;
      event correlator=2;
      database=sample;userid=testuser;authid=TESTUSER;
      origin node=0;coordinator node=0;
      application id=NF000001.LC12.041221184115;application 
    name=db2jcc_application;
    ...

    以 testuser2 身份(这个用户 ID 不具有访问 EMPLOYEE 表的权限)使用连接;请注意授权冲突:

    timestamp=2004-12-21-13.41.37.015000;category=CONTEXT;audit 
    event=PREPARE;
      event correlator=3;
      database=sample;userid=testuser2;authid=TESTUSER2;
      origin node=0;coordinator node=0;
      application id=NF000001.B912.041221184137;application 
    name=db2jcc_application;
      package schema=NULLID;package name=SYSSN300;
      package section=1;text=SELECT * FROM EMPLOYEE;
    timestamp=2004-12-21-
    13.41.37.085000;category=CHECKING;audit 
    event=CHECKING_OBJECT;
      event correlator=3;event status=-551;
      database=sample;userid=testuser2;authid=TESTUSER2;
      origin node=0;coordinator node=0;
      application id=NF000001.B912.041221184137;application 
    name=db2jcc_application;
      package schema=NULLID;package name=SYSSN300;
      package section=1;object schema=ADMIN;object 
    name=EMPLOYEE;object type=TABLE;
      access approval reason=DENIED;access attempted=SELECT;
    ...

    我们看到一个连接从 testuser2 更改为 testuser3:

    timestamp=2004-12-21-13.43.26.753000;category=CONTEXT;audit 
    event=CONNECT_RESET;
      event correlator=5;
      database=sample;userid=testuser2;authid=TESTUSER2;
      origin node=0;coordinator node=0;
      application id=NF000001.B912.041221184137;application 
    name=db2jcc_application;
      
    timestamp=2004-12-21-13.43.26.993000;category=CONTEXT;audit 
    event=CONNECT;
      event correlator=2;
      database=sample;userid=testuser3;authid=TESTUSER3;
      origin node=0;coordinator node=0;
      application id=NF000001.B912.041221184326;application 
    name=db2jcc_application;

Oracle 配置

与 DB2 一样,我们假设示例 Oracle 数据库是随一些示例用户安装的。然而,与 DB2 不同的是,这里需要其他一些配置才能利用 Oracle VPD 功能。以下简要总结这些步骤:

  1. 以 sysdba 身份连接到 Oracle。
  2. 添加 VPD 策略以将 pred_function 附加到 EMP 上的所有 SQL SELECT/UPDATES:
    	begin
    		DBMS_RLS.ADD_POLICY (
    		OBJECT_SCHEMA => 'SCOTT',
    		OBJECT_NAME => 'EMP',
    		POLICY_NAME => 'DS_EX',
    		FUNCTION_SCHEMA => 'SCOTT',
    		POLICY_FUNCTION => 'PRED_FUNCTION',
    		STATEMENT_TYPES => 'SELECT,UPDATE');
    	end;
    /    (the "/" to the left here is very important)
  3. 定义一个 pred 函数以执行筛选:
        CREATE OR REPLACE function scott.pred_function
    	(p_schema in varchar2 default NULL, 
    	p_object in varchar2 default NULL)
        RETURN varchar2
        AS
        l_client_id varchar2(30) :=
    		nvl(sys_context('userenv',
    		'client_identifier'), 
    		' NULL');
        l_user varchar2(30) := sys_context('userenv',
    		'session_user');
        BEGIN
    	return 'ename = ' || 'sys_context(''userenv'',
    	''client_identifier'')';
        END;
     /
  4. 以 EMP 表中的两个雇员——SCOTT 和 BLAKE——测试 VPD:
        exec DBMS_SESSION.SET_IDENTIFIER('SCOTT');
        select sys_context('userenv',
    	'client_identifier') from dual;
        select pred_function from dual;
        select ename, sal from scott.emp;
    ENAME             SAL
    ---------- ----------
    SCOTT            3000
        exec DBMS_SESSION.SET_IDENTIFIER('BLAKE');
        select sys_context('userenv',
    	'client_identifier') from dual;
        select pred_function from dual;
        select ename, sal from scott.emp;
    ENAME             SAL
    ---------- ----------
    BLAKE            2850

    如果您上面没有定义 VPD 过程,则相同的选择会返回整个表的内容。
  5. 启用审核。要启用审核,首先必须在数据库管理器中将 AUDIT_TRAIL 配置为 OS 或 DB(选择适当的一个)。在我们的示例中,我们将审核设置为使用 OS 审核,它将审核信息发送到 Windows 事件日志。然后配置 Oracle 以审核表访问:audit select table by access;
  6. 在运行示例应用程序中的一些测试之后,我们将在事件日志中看到这些消息类型。这表明 Oracle 审核正确地反映我们的标识断言:
    Audit trail: SESSIONID: "194" ENTRYID: "6" STATEMENT: "6" USERID: "SYSTEM" 
    TERMINAL: "unknown" ACTION: "3" RETURNCODE: "0" OBJ$CREATOR: "SYS" 
    OBJ$NAME: "DUAL" OS$USERID: "Admin" SES$LABEL: "DB2ADMIN".
    Audit trail: SESSIONID: "193" ENTRYID: "30" STATEMENT: "9" USERID: "SYSTEM" 
    TERMINAL: "unknown" ACTION: "3" RETURNCODE: "0" OBJ$CREATOR: "SCOTT" 
    OBJ$NAME: "EMP" OS$USERID: "Admin" SES$LABEL: "SCOTT".

使用示例应用程序

数据库准备好之后,现在就可以对应用程序的功能进行测试。

为演示数据源重验证功能而在本文中包含的应用程序(TestAuthorizingDSApp.ear,可从下载文件中获得)应该使用上面描述的步骤进行安装和配置。此应用程序使用 EMPLOYEE 示例表(它在缺省情况下是在 DB2 的安装过程中安装的)和 J2EE 安全性,并使用基于 J2EE 窗体的登录方式加以保护。(也包含了一个针对 Oracle 的示例应用程序版本 TestAuthOraDSApp.ear。)

要运行该示例应用程序,您应该:

  1. 安装该应用程序并启动承载 TestAuthorizingDSApp.ear 的应用服务器。
  2. 在浏览器窗口中通过此 URL:http://<hostname>:<port>/TestAuthDSWeb 打开登录页面,启动测试数据源重验证。这样将显示图 17 的画面。
    图 17. 示例应用程序主页面
    图 17. 示例应用程序主页面
  3. 使用简单的 JDBC 调用或 CMP EJB 来检查 DataSource 重验证功能。如果您使用 JDBC 调用选择 Access DataSource 作为 Authenticated User,则执行 JDBC 代码的结果将会以应用程序的当前已通过身份验证的用户来显示。因为您还没通过身份验证,所以首先显示给您的是登录页面(图 18)。
    图 18. 示例应用程序登录页面
    图 18. 示例应用程序登录页面
  4. 在登录页面,输入 WebSphere Application Server 注册表中能够访问 SAMPLE 的一个用户的用户 ID 和密码,然后单击 Login。请记住,这意味着您需要配置 DB2 授权才能让该用户访问数据库。(有关为数据库配置表级授权的详细信息,请参考 DB2 文档。)

    我们假设 DB2 和 WebSphere Application Server 共享公共用户。通过使用相同的注册表(例如,Windows® 注册表)或者通过其他某种方式确保注册表中的用户 ID 保持同步可以实现这一点。

  5. 既然您已经以能够访问 SAMPLE 数据库的用户登录了,您应该能够看到如图 19 所示的内容。
    图 19. 用户访问摘要
    图 19. 用户访问摘要
    JDBC 代码的用处不大,它只是从 EMPLOYEE 表中提取所有用户。如果成功,则会将表中的所有雇员显示在结果页的一个 TextArea 中。为了方便再测试,这个页面也包含了两个按钮:
    • Again——回到主页面以便再次执行相同的测试,或者,也可以使用 CMP 实体 Bean 来执行测试。
    • Access DataSource as Authenticated User using CMP calls——将显示图 20 的画面。
    图 20. 使用 CMP 实体 Bean 的用户访问总结
    图 20. 使用 CMP 实体 Bean 的用户访问总结

    结果页再次包含表中的所有雇员,但这次是使用 CMP 实体 Bean 来访问表的。

  6. 以上步骤显示了一个已通过身份验证的具有访问数据库表权限的用户如何访问 EMPLOYEE 表中的数据。要查看当一个已通过身份验证的不具有这些权限的 WebSphere Application Server 用户试图使用此应用程序时会发生什么结果,请单击当前页中的 Logout 按钮并回到登录页面(图 21)。
    图 21. 登录页面
    图 21. 登录页面
  7. 这次登录时所用的用户 ID 能够访问应用程序,但不能访问 SAMPLE 数据库。这样将会使您回到应用程序的主页面。
  8. 选择 Access DataSource as Authenticated User using JDBC calls。将显示图 22 的画面。
    图 22. 用户访问摘要失败
    图 22. 用户访问摘要失败

    此操作无法从 EMPLOYEE 表提取任何数据,因为用户 scott 无法访问该表。

  9. 使用 Again 按钮返回到主页面并使用 CMP 实体 Bean 来重试测试。
    图 23. 使用 CMP 实体 Bean 的用户访问摘要失败
    图 23. 使用 CMP 实体 Bean 的用户访问摘要失败
  10. 使用 CMP 实体 Bean 也会产生预期的异常。如果您打算将重验证和 CMP 缓存结合使用,请参阅 CMP 缓存考虑事项
  11. 主页面中的 Access DataSource as an Unauthenticated User 按钮将注销当前用户然后使用 JDBC 调用来访问 EMPLOYEE 表。当一个未经过身份验证的用户访问数据库时,它将在第一个获取连接的用户标识——也就是与前面配置的 J2C 身份验证别名相关联的用户 ID 和密码——中执行。当您点击此按钮时,您应该能看到图 24 所示的画面。
    图 24. 获取连接的用户的用户访问摘要
    图 24. 获取连接的用户的用户访问摘要

故障诊断

如同 WebSphere Application Server 代码,我们在本文中提供的数据库重验证助手类使用 JRAS 跟踪工具进行跟踪(不推荐使用 JRAS 用法,但我们这里暂时使用了它)。跟踪什么——以及跟踪的详细级别——是由使用 WebSphere Application Server 管理工具指定的跟踪字符串控制的。在对数据库重验证进行故障诊断方面,以下跟踪字符串被证明是非常有用的:

  • WAS.j2c=all=enabled
    启用 WebSphere Application Server 连接器模块操作的日志记录。对此组件记录的信息包括分配连接、将连接指派给事务以及以后返回到自由池的信息。
  • RRA=all=enabled
    启用数据库适配器跟踪。它跟踪容器与 JDBC 驱动程序的交互信息,并记录执行 SQL 语句所做的调用和配置数据存储助手类所做的调用。
  • com.ibm.swservices.*=all=enabled
    启用置入重验证数据存储助手类的诊断日志记录

为了防止用户错误地启用重验证而没有提供重验证实现,WebSphere Application Server V6.0.1 加入这样的逻辑:如果在 DataSource 中启用重验证但 doConnectionSetupPerTransaction() 方法不提供自定义实现,则会引发 SQLException。

对重验证失败进行故障诊断是很困难的,特别是在一些情况下,如使用 DB2 时实际的底层错误在典型的 WebSphere Application Server 日志中不可见的情况。例如,如果调用 resetDB2Connection 时指定无效的信息,则在一般的 WebSphere Application Server 错误输出中看不到原因。实际上需要启用 WebSphere Application Server 跟踪(如前面所指出的)才能查看真正的 DB2 错误消息。以下示例输出是从一个测试中提取出来的,这个测试为连接用户指定了无效的密码。这里是正常的 WebSphere Application Server 错误输出 (SystemOut.log):

[5/16/05 19:14:42:290 EDT] 0000002a ConnectionEve A   J2CA0056I: The 
Connection Manager received a fatal connection error from the Resource 
Adaptor for resource jdbc/DB2AuthDS.  The exception which was received 
is com.ibm.websphere.ce.cm.StaleConnectionException: An error occurred 
during a deferred connect reset and the connection has been terminated.  
See chained exceptions for details.  DB2ConnectionCorrelator: 
NF000001.E511.050516231431

从上面可以看出,WebSphere Application Server 并没有打印出有意义的错误消息,真正的错误只能通过启用跟踪才能发现。以下是对相同事件的跟踪,但要注意,它并没有指出确切的错误:

[5/16/05 19:14:42:290 EDT] 0000002a GenericDataSt <  
mapExceptionHelper: Mapping was done returning: Exit
com.ibm.db2.jcc.am.DisconnectException: An error occurred during 
a deferred connect reset and the connection has been terminated.  
See chained exceptions for details.  DB2ConnectionCorrelator: 
NF000001.E511.050516231431
	at 
com.ibm.db2.jcc.t4.T4Agent.readDeferredResetConnection(T4Agent.java:501)
	at 
com.ibm.db2.jcc.t4.T4Agent.beginReadChain(T4Agent.java:513)
	at 
com.ibm.db2.jcc.am.Agent.flow(Agent.java:252)
	at 
com.ibm.db2.jcc.am.PreparedStatement.flowExecute(PreparedStatement.java:2209)
	at 
com.ibm.db2.jcc.am.PreparedStatement.executeQueryX(PreparedStatement.java:505)
	at 
com.ibm.db2.jcc.am.PreparedStatement.executeQuery(PreparedStatement.java:488)
	at 
com.ibm.ws.rsadapter.jdbc.WSJdbcPreparedStatement.pmiExecuteQuery
(WSJdbcPreparedStatement.java:667)
	at 
com.ibm.ws.rsadapter.jdbc.WSJdbcPreparedStatement.executeQuery
(WSJdbcPreparedStatement.java:477)
	at 
com.myd.session.TestAuthDSTasksBean.testDB2DS(TestAuthDSTasksBean.java:64)
	at 
com.myd.session.EJSRemoteStatelessTestAuthDSTasks_19d9d6be.testDB2DS
(EJSRemoteStatelessTestAuthDSTasks
_19d9d6be.java:66)
	at 
com.myd.session._TestAuthDSTasks_Stub.testDB2DS(_TestAuthDSTasks_Stub.java:297)
	at 
com.ibm.swservices.servlet.DBIdAssertionServlet.doGet(DBIdAssertionServlet.java:41)
	at 
javax.servlet.http.HttpServlet.service(HttpServlet.java:743)
	at 
javax.servlet.http.HttpServlet.service(HttpServlet.java:856)
	at 
com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1216)
	at 
com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:630)
	at 
com.ibm.ws.webcontainer.servlet.CacheServletWrapper.handleRequest
(CacheServletWrapper.java:80)
	at 
com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:1752)
	at 
com.ibm.ws.webcontainer.channel.WCChannelLink.ready(WCChannelLink.java:77)
	at 
com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleDiscrimination
(HttpInboundLink.java:466)
	at 
com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleNewInformation
(HttpInboundLink.java:405)
	at 
com.ibm.ws.http.channel.inbound.impl.HttpICLReadCallback.complete
(HttpICLReadCallback.java:104)
	at 
com.ibm.ws.tcp.channel.impl.WorkQueueManager.requestComplete
(WorkQueueManager.java:555)
	at 
com.ibm.ws.tcp.channel.impl.WorkQueueManager.attemptIO(WorkQueueManager.java:608)
	at 
com.ibm.ws.tcp.channel.impl.WorkQueueManager.workerRun(WorkQueueManager.java:941)
	at 
com.ibm.ws.tcp.channel.impl.WorkQueueManager$Worker.run(WorkQueueManager.java:1028)
	at 
com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1394)
---- Begin backtrace for Nested Throwables
com.ibm.db2.jcc.am.SqlException: Connection authorization failure 
occurred.  Reason: password invalid.
	at 
com.ibm.db2.jcc.t4.T4Connection.mapSecchkcd(T4Connection.java:1773)
	at 
com.ibm.db2.jcc.t4.T4Connection.securityCheckComplete(T4Connection.java:1365)
	... rest deleted ...

版本

测试

我们已经通过直接的 JDBC 和 CMP Bean 使用 DB2 和 Oracle CF 对这里提供的代码示例进行测试,测试是使用 WebSphere Application Server 分布式地在 AIX® 和 Windows 平台上进行的。相同的技术也可用于提供的其他数据库,它们支持如下扩展:允许在现有连接上声明用户标识信息。

DB2
我们的测试使用 DB2 UDB V8.1 FP 9(加上 APAR IY71420;此 APAR 修复了一个小错误:resetDB2Connection() 使用不遵循 DataSource 中的缺省架构设置)。此补丁和 APAR 都是必需的,因为它们共同包含了需要的 resetDB2Connection() API。我们在 AIX 和 Windows 中,以及 z/OS®(而非 iSeries™)上的 DB2 中使用带有 DB2 UDB 的通用驱动程序(JCC 驱动程序)。我们的测试使用 XA 和非 XA 模式,以及 type-2 和 type-4 模式来执行。

Oracle
我们在没有问题的 Windows 中,使用瘦的非 XA 驱动程序,在 Oracle 9i Release 2(个人版)的环境下测试我们的示例代码。我们有理由相信测试可以在其他操作系统、其他 Oracle 版本或更高版本的 Oracle 中进行。

性能

为了测试数据库标识传播的性能,我们使用在本文中提供的示例应用程序的“Access DataSource as Authenticated User using JDBC calls”功能。此功能非常简单,它执行单条的 SQL 语句:

SELECT * FROM EMPLOYEE

性能的衡量是使用一个负载生成器工具来进行的,该工具模拟 12 个并发用户反复执行以上功能。将连接池的大小设置为 10,以便能够对连接进行周期性地重验证。

请记住,此示例并不能恰当地反映最真实的实际环境。对于这个非常简单的测试场景,重验证的相对成本远比实际应用程序中的期望值高。实际应用程序中有三个关键项可能不同:

  • SQL 查询可能更复杂(因此成本更高)。
  • 数据库更大(我们的数据库只有 50 行)。
  • 每个用户请求执行的 SQL 查询次数通常大于 1。

因此,在实际应用程序中每次重验证完成的“实际”工作量将大幅提升,使得重验证的相对成本比我们的简单测试情况低。在对 DB2 和 Oracle 进行的有限性能测试中,我们发现执行重验证的开销差别在 15% 至 50% 之间,这取决于包括数据库、系统和测试方案在内的大量因素。也就是说,在我们的极为简单(也是最坏情况)的情况中,发现吞吐量下降 15% 至 50% 之间。

这里给出的数字不是正规严格受控的基准所产生的结果,针法代表任何特定的应用程序负载。您应该在自己的应用程序中进行测试以确定在您的环境中的性能影响。

(z/OS 中的 DB2 V8 APAR PQ99707 包含使大量重验证更加有效的性能增强功能。此 APAR 只适用于 z/OS 平台。我们无法对该 APAR 进行测试。)

8.3 其他版本和功能

DB2 V9 将包含内置的细粒度的授权支持,被称为 Label Based Access Control (LBAC)。使用这些功能以及本文中描述的内容,将能够基于对 WebSphere Application Server 进行身份验证的用户实现细粒度的授权,与 Oracle 所提供的授权类似。zSeries 平台上的 DB2 当前支持多级访问控制 (MLS)。

IBM 正计划对 DB2 进行增强以提供一个更有效的跨所有平台的重验证框架。正如前面提到的,zSeries 平台上的重验证已经相当有效。


免责声明

虽然我们这里已对该方案进行充分的测试,相信它能胜任一般情况,并且这里描述的 API 是它们各自的产品中正式支持的,但我们没有执行广泛的负载测试和对每种可能情况进行详细分析。(作者欢迎您反馈阅读本文的感受。)以下是在您的环境中应用此解决方案之前应该考虑的一些事项。

未探讨数据库安全性配置

本文不是数据库安全性的教程。作者除了演示本文讨论的功能外,没有以任何合理的方式配置数据库安全性。您应该与有经验的 DBA 共同合理地配置数据库安全性和利用本文讨论的功能。

信任问题

在使用此解决方案时,需要意识到,您基本要求数据库信任应用服务器能够向它提供最终用户标识信息,这是很重要的。因此,您信任在该应用服务器上运行的应用程序能够适当地执行此标识保护。Leveraging Oracle Database Security with J2EE Container Managed Persistence (请参阅参考资料)以另一种方式来表述此观点:

请注意,此设计意味着数据库和应用程序之间存在一个共享信任。这在几乎所有情况下都是非常必要的。并非 100% 信任,但需要一定的信任。如果您不信任您的应用程序(至少有一点),则不会让它们连接到您的数据库!否则,您将需要使用一个类似于这里描述的受限信任模型。

请记住,虽然此解决方案要求您信任应用程序能够执行标识保护,但与以前的每个应用程序只是使用单一的用户标识访问数据库的架构相比,这仍然是一种提高。在那种架构中只是完成了信任,您还不能实现它。

CMP 缓存考虑事项

在实现重验证时必须考虑 WebSphere Application Server CMP Bean 的缓存功能。如果 WebSphere Application Server 在事务范围外缓存 CMP 数据,则需要警惕可能出现本文未能解决的访问冲突。缺省情况下 CMP Bean 不跨事务缓存,但可以为个别 Bean 打开缓存并在缓存中控制数据的生存周期(请参考 WebSphere Application Server documentation 中的“Lifetime In Cach”)。缓存不会因用户 ID 而有差异。这意味着一个用户建立的 CMP(通过缓存)可能被未经授权访问数据库中该数据的其他用户看到。这不影响 Bean 的创建、更新和删除,因为它们始终流向数据库,因此始终在当前用户 ID 下执行。考虑到此行为,对于高度安全的 CMP,您可能选择不打开缓存,而如果您的目标只是审核创建、更新和删除(其中对谁查询 Bean 的审核并不重要),则可以打开缓存。


结束语

本文向您介绍如何“获取蛋糕并享用它”。采用 WebSphere Application Server V6 中的新功能,以及本文中的示例,您可以编写遵循 J2EE 的应用程序,同时又利用数据库特定的授权和审核基础设施。


致谢

作者真诚感谢 IBM Software Services for WebSphere 的 Art Jolin 提出的有价值的意见,以及 Tom Alcott 对本文的审阅。我们也要感谢 IMB DB2 团队的几位成员:Paul Ostler、Sherry Guo、Paul Bird 和 Curt Cotner。正是因为他们的帮助和积极参与才使本文得以问世。


下载

描述名字大小
代码样例was6_databaseidentityprop.ZIP  ( HTTP | FTP )351 KB

参考资料

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere
ArticleID=91006
ArticleTitle=IBM WebSphere 开发者技术期刊: WebSphere Application Server V6 中的数据库标识传播
publish-date=06152005