使用凭证保险库为 portlet 提供单点登录
引言
在 WebSpheret Portal 服务器上运行的一些 portle 需要向后端远程应用程序提交凭证(例如用户标识和密码)。如果远程应用程序使用的凭证和 WebSphere Portal 使用的凭证相同,则这些 portlet 可以重用这些凭证。portlet 可以使用 Java 验证和授权服务(Java Authentication and Authorization Service,JAAS)API 抽取这些凭证,然后将它们提交给远程应用程序。然而,远程应用程序使用的凭证并不总是可能与 WebSphere Portal 使用的凭证相同。因此,为了使用户能够体验单点登录,WebSphere Portal V 4.1 Enable Offering 提供一种凭证保险库(credential vault)机制,portlet 可以使用这种机制安全地设置和检索凭证。
本文提供了四种 portlet 应用程序并对它们进行了解释,这四种 portlet 使用不同的凭证保险库技术来维护凭证。开发者可以用这些应用程序作为模型来编写自己的 portlet。读者应该对开发和安装 portlet 以及定制页面有一个基本的了解。
了解凭证保险库
凭证保险库(credential vault)是存储凭证的资源库。凭证的示例包括证书、私钥、用户标识以及密码。WebSphere Portal 提供一个名为 CredentialVaultService的类,portlet 可以用它存储凭证并从保险库检索凭证。
保险库适配器(vault adapter)是凭证保险库和 CredentialVaultService之间的一个插件模块。WebSphere Portal 提供了一个用门户数据库作为缺省保险库的简单的适配器,还提供了另一个用于 Tivoli Access Manager 的适配器。在门户框架中,您可以继承 VaultAdapter类,从而开发一个到您自己的资源库的适配器接口。您需要在保险库配置文件 WebSphere\AppServer\lib\app\config\services\VaultService.properties中指定任何新创建的适配器。
保险库段(vault segment)是保险库的一个分区。段有两种类型:用户管理的和管理员管理的。门户管理员可以使用 Portal Administration 区域的 Security 页面上的 Credential Vault 选项卡创建管理员管理的段。这个选项卡名为 Credential Vault portlet。WebSphere Portal 在缺省保险库中提供一个用户管理的段。
保险库槽(vault slot)是保险库段的一部分;它是用 CredentialSlotConfig类表示的。portlet 用保险库槽来存储和检索凭证。在用户管理的段中,您可以用编程方式创建一个保险库槽。管理员还可以用 Credential Vault portlet 在管理员管理的段中创建一个槽。这个 portlet 可以设置和获取以任一种方式创建的槽中的凭证。 CredentialSlotConfig对象包含有关槽的配置信息;例如槽标识、段对象标识以及其他属性。
凭证有两种类型:主动的(active)和被动的(passive)。被动凭证允许 portlet 抽取凭证的机密。主动凭证不允许 portlet 抽取凭证的机密。不过,主动凭证可以使用标准认证(如基于 HTTP 表单的认证或基本认证)向后端应用程序提交凭证。您可以研究 WebSphere Portal API 文档中的 com.ibm.wps.portletservice.credentialvault.credentials包以了解更多关于凭证的信息。
下面几部分中所描述的 portlet 使用以编程方式创建的保险库槽,还使用管理员在缺省保险库中创建的保险库槽。下面描述的所有 portlet 均使用被动凭证。
样本 portlet 应用程序
本文提供了四种样本 portlet 应用程序的代码示例。您可以安装这些应用程序,从而了解创建保险库槽以及设置凭证并将凭证放入保险库槽中的不同方式。
WebSphere Portal 提供的凭证保险库定义了四种保险库凭证类型:
portlet 专用槽(Portlet private slot)
存储那些不在 portlet 之间共享的用户凭证。专用槽 Portlet 应用程序演示了专用槽。
共享槽(Shared slot)
存储那些在用户的 portlet 之间共享的用户凭证。共享槽 Portlet 应用程序演示了共享槽。
管理槽(Administrative slot)
允许每个用户存储一个管理员定义的资源(例如 Lotus Notes)的机密。管理槽 Portlet 应用程序演示了管理槽。
系统槽(System slot)
存储所有用户和 portlet 共享实际机密的系统凭证。系统槽 Portlet 应用程序演示了系统槽。
属于所有这些样本应用程序(除系统槽 Portlet 应用程序外)的 portlet 既支持编辑(edit)模式,也支持查看(view)模式。用户可以选择编辑模式以将凭证存储到保险库中,也可以选择查看模式来查看已被设置的凭证。
管理员必须为系统槽 Portlet 应用程序和管理槽 Portlet 应用程序创建槽。针对系统槽 Portlet 应用程序,在查看模式下,portlet 显示一个共享的用户标识和密码对。
当用户首次使用编辑模式提交凭证时,专用槽 Portlet 应用程序和共享槽 Portlet 应用程序以编程方式创建槽。
所有这些样本应用程序共同演示了如何存储类型为用户标识和密码的被动凭证。
专用槽 Portlet 应用程序
专用槽 Portlet 应用程序只包含一个 portlet,即 PrivateSlotSamplePortlet。因为这个 portlet 创建一个专用槽,所以只有这个 portlet 实例可以访问该槽中的凭证;用户的其他 portlet 不能访问这个槽。这个应用程序阐述了如何实现下列操作:
· 初始化 CredentialVaultService
· 创建一个专用槽并设置 凭证
· 访问专用槽中的凭证
这个应用程序的代码包含在附带的 privateslotsample.war中。这个 WAR 文件的 WEB-INF\classes\samplepkg目录下包含了 PrivateSlotSamplePortlet.java类。
POP3 邮件 portlet 是一个特别适合使用专用保险库槽的示例。例如,假设一名用户:
· 定制页面时添加了两个 POP3 portlet 实例
· 将一个实例连接到商业电子邮件帐户上
· 将另一个实例连接到个人电子邮件帐户上
当 portlet 创建一个专用槽时,每个实例存储自己的凭证。虽然两个实例使用相同的资源名创建专用槽,但凭证保险库在内部将唯一的 portlet 实例标识附加到资源名上。如果开发者构建一个连接到 POP3 邮件帐户的 portlet,那么这个 portlet 可能会用专用槽独立存储凭证。
初始化 CredentialVaultService
CredentialVaultService是一个 portlet 服务,portlet 可以用它来创建用于存储和检索凭证的保险库槽。清单 1 说明了如何将一个服务对象初始化为数据成员 vaultService。成员方法对它进行多次访问。
清单 1:初始化 CredentialVaultService对象
super.initConcrete(settings);
try{
vaultService = (CredentialVaultService) getPortletConfig().getContext().
getService(CredentialVaultService.class);
}
catch(Exception e){
return;
}
}
创建专用保险库槽并设置凭证
我们假设用户定制页面时添加了一个 PrivateSlotSamplePortletLet 实例。如果用户接着选择编辑模式图标,那么将显示一个表单,提示他输入用户标识和密码。在用户提交了用户标识和密码之后,Portal 调用 actionPerformed方法,如清单 2 所示。用户第一次提交表单时,PrivateSlotSamplePortlet 使用带有下列参数的 createSlot方法创建专用保险库槽。
resourceName
后端应用程序的名称
segmentID
需要创建槽的保险库段的标识。这个段应该是一个用户管理的段。下面的代码用 vaultService.getDefaultUserVaultSegmentId()获取这个值。
descriptionMap, keywordMap
分别描述以语言环境为键的保险库槽以及关键字。在样本代码中,它们被设置为空映射对象。
secretType
CredentialVaultService 中定义的一个常量。因为需要保险库槽来存储用户标识和密码,所以它被设置为 vaultService.SECRET_TYPE_USERID_STRING_PASSWORD_STRING。
active
判断凭证是主动的(值为 true)还是被动的(值为 false)。因为样本 portlet 需要检索用户标识和密码,所以它被设置为 false。
portletPrivate
判断凭证机密是特定于 portlet 的(值为 true)还是一个用户的所有 portlet 共享的(值为 false)。它被设置为 true,这样仅允许来自创建槽的 portlet 实例的访问。
createSlot
返回的对象是 CredentialSlotConfig类型的,该方法用于获取对 slotID 的访问权。 这个 slotID 存储在 PortletData 中。它必须是持久的,这样 portlet 才可以访问和更新凭证。 setCredentialSecretUserPassword方法使用 slotID 将用户标识和密码存储到凭证保险库中。清单 2 说明了如何创建一个专用保险库槽并将凭证存储到槽中。
清单 2:创建一个保险库槽并存储凭证
public void actionPerformed(ActionEvent event) throws PortletException {
DefaultPortletAction action = (DefaultPortletAction) event.getAction();
if( (action == null) || (!action.getName().equals("save") ) )
return;
PortletRequest portletRequest = event.getRequest();
String userID = (String) portletRequest.getParameter("userID");
String password = (String) portletRequest.getParameter("password");
// save only if both parameter are set
if(userID ==null || password ==null || userID.trim().equals("")
|| password.trim().equals(""))
return;
try{
PortletData data = portletRequest.getData();
String slotId = (String) data.getAttribute("PrivateSlotSamplePortletSlotID");
if(slotId==null) { // create slot if necessary
String resourceName = "POP3MailApp";
ObjectID segmentID = vaultService.getDefaultUserVaultSegmentId();
Map descripMap = new Hashtable();
Map keywordMap = new Hashtable();
int secretType = vaultService.SECRET_TYPE_USERID_STRING_PASSWORD_STRING;//用户名密码
boolean active = false;//判断凭证是主动的(true)还是被动的(false)
boolean portletPrivate = true;//判断凭证机密是特定portlet(true)用户共享的portlet(false)
//create the slot
CredentialSlotConfig slot= vaultService.createSlot
(resourceName, segmentID,descripMap,
keywordMap, secretType, active, portletPrivate, portletRequest);
slotId=slot.getSlotId();
data.setAttribute("PrivateSlotSamplePortletSlotID", slot.getSlotId());
data.store();
}
// store credentials in vault
vaultService.setCredentialSecretUserPassword(slotId, userID,
password.toCharArray(),portletRequest);
}
catch(Exception e){
e.printStackTrace(System.out);
}
}
访问凭证
在前一部分中,您看到了如何创建一个保险库槽并将凭证存储在其中。这一部分将阐述如何从凭证槽中抽取存储的凭证。
PrivateSlotSamplePortlet使用 getAttribute方法从 PortletData对象中检索 slotID。 getCredential方法要求 slotID 作为其参数。 getCredential方法首先要确保存在 slotID。如果不存在 slotID, getCredential方法就会退出。因为存储的凭证(具体来说就是用户标识和密码)是被动的,所以表示类型的参数被设置为 UserPasswordPassive。
getCredential方法返回一个 UserPasswordPassiveCredential类型的对象,其中包含用户标识和密码。 doView方法和 doEdit方法都调用 getCredential方法,前者在 portlet 窗口中显示存储的用户标识和密码,后者将这些值植入编辑表单的文本域中。
清单 3:从专用槽中抽取凭证
private void getCredential(PortletRequest portletRequest,StringBuffer userID,StringBuffer password) throws PortletServiceException {
try{
String slotId = (String) portletRequest.getData().getAttribute
("PrivateSlotSamplePortletSlotID");
if(slotId==null)
return ;
UserPasswordPassiveCredential credential=(UserPasswordPassiveCredential)vaultService.getCredential
(slotId, "UserPasswordPassive", new HashMap(), portletRequest);
userid.append(credential.getUserId() );
password.append( String.valueOf(credential.getPassword() ) );
}
catch(com.ibm.wps.portletservice.credentialvault.CredentialSecretNotSetException e){
return ;
}
}
运行专用槽 Portlet 应用程序
请将附带的专用槽 Portlet 应用程序安装到 WebSphere Portal 版本 4.1.2 中。定制一个页面,在该页面上添加两个 PrivateSlotSample。Portlet实例
针对 portlet 的一个实例,选择编辑模式并提交用户标识和密码。这个 portlet 创建一个保险库槽并将用户标识和密码存储到凭证保险库中。只有这个 portlet 实例从凭证中抽取用户标识和密码并显示它们。
遵循类似的步骤用另一个 portlet 实例提交用户标识和密码。每个 portlet 实例显示它唯一的用户标识和密码(或者说它自己的凭证)。图 1 显示了一个定制的页面,它带有两个 PrivateSlotSamplePortlet实例,每个实例都显示自己的用户标识和密码。
图 1:拥有不同凭证的两个 PrivateSlotSamplePortlet实例
共享槽 Portlet 应用程序
共享槽 Portlet 应用程序包含两个 portlet, SharedSlotSamplePortlet1和SharedSlotSamplePortlet2。除类名称外,两个 portlet 的代码完全相同。
当用户先从其中一个 portlet 提交他的用户标识和密码时,这个 portlet 创建一个共享保险库槽并将用户凭证存储在其中。这个共享槽允许其他 portlet 检索和显示凭证。这个 portlet 应用程序演示了如何创建共享保险库槽、访问共享槽以及设置和获取凭证。您可以下载 sharedslotsample.war文件并打开它,以便研究这个应用程序的代码。这个 WAR 文件的 WEB-INF\classes\samplepkg目录下包含了 SharedSlotSamplePortlet1.java类和 SharedSlotSamplePortlet2.java类。
当公司使用相同的内部网用户标识和密码来授权用户访问多个后端应用程序时,这种创建共享槽的能力就很有用了。如果有多个 portlet 连接到后端应用程序,用户就不需要为每个 portlet 指定他的用户标识和密码了。一个 portlet 可以在一个共享槽中设置用户凭证,其他的 portlet 可以访问它以检索用户凭证。
创建共享保险库槽
创建共享保险库槽与创建专用槽类似。然而,您将 portletPrivate(被作为 createSlot方法的参数传递)标志设置为 false,以便在所有与单个用户相关联的 portlet 实例之间共享保险库槽。portlet 可以从 portlet.xml 获取用于 createSlot方法的 resourceName参数。
访问共享槽
如果用户在编辑模式下为 SharedSlotSamplePortlet1或 SharedSlotSamplePortlet2设置凭证,那么在查看模式下,portlet 显示凭证。如果用户设置凭证,则每个 portlet 的 doView方法调用 getCredentialSlotConfig方法以获得一个 CredentialSlotConfig类型的对象。在 getCredentialSlotConfig方法中调用 getAccessibleSlots方法,它返回一个包含 CredentialSlotConfig类型对象的迭代器。您可以检查这个迭代器以确定它是否包含这样的 CredentialSlotConfig对象,这个对象与创建槽时传送的同一个资源名称相关联。因为 WebSphere Portal 将用户的对象标识附加到资源名称上,所以应该使用 String类 startswith方法来比较抽取的资源名称,从而确定它是否与资源匹配。清单 4 演示了如何查找槽。
清单 4:查找与资源名相对应的槽
private CredentialSlotConfig getCredentialSlotConfig(PortletRequest portletRequest) {
java.util.Iterator it = null;
try{ // creates list of accessible slots
it= vaultService.getAccessibleSlots(portletRequest);
}catch(PortletException pe)
{
return null;
}
CredentialSlotConfig config =null ;
String curResName = null;
while(it.hasNext() )
{
config =(CredentialSlotConfig )it.next() ;
curResName = config.getResourceName();
//searches for shared resource name
if (curResName.startsWith(resourceName ) )
return config;
}
return null;
}
使用共享槽来设置和获取凭证
portlet 使用共享槽来存储和检索凭证,其方式与它们使用专用槽时的方式相同。它们用 getCredentialSlotConfig方法访问 CredentialSlotConfig对象,从中抽取 slotID。
安装和定制共享槽 Portlet 应用程序
1. 安装共享槽 Portlet 应用程序,并定制一个页面,在该个页面上添加一个 SharedSlotSamplePortlet1实例和一个 SharedSlotSamplePortlet2实例。
2. 针对其中一个 portlet,选择 edit模式。
3. 提交用户标识和密码。
选择的 portlet 创建一个共享保险库槽,并将用户标识和密码凭证存储到凭证保险库中。另一个 portlet 使用共享保险库槽从保险库中抽取凭证,并显示相同的用户标识和密码。
图 2:拥有相同凭证的 SharedSlotSamplePortlet1实例和 SharedSlotSamplePortlet2实例
管理槽 Portlet 应用程序
管理槽 Portlet 应用程序包括一个 portlet,即 AdminSlotSamplePortlet。与专用槽 Portlet 应用程序和共享槽 Portlet 应用程序不同,在这个样本应用程序中,保险库槽不是在 portlet 代码中创建的。相反,portlet 希望已经使用凭证保险库 portlet 创建了一个保险库槽。它查找这样一个槽名称:它作为 portlet.xml中具体的portlet 的配置参数。
当管理员创建保险库槽时,他确保未选中 Vault slot is shared复选框,这表示用户不能共享这个槽。虽然与不同的用户相关联的 portlet 实例不能共享这个槽,但是单个用户的所有 portlet 实例之间可以共享这个槽。
这个应用程序中的样本 portlet 用 API 设置和获取使用 CredentialVaultService 的凭证。这个 portlet 应用程序演示了如何创建一个管理槽、配置槽名称以及设置和获取凭证。这个应用程序的代码包含在 adminslotsample.war中,该文件的 WEB-INF\classes\samplepkg目录下包含了 AdminSlotSamplePortlet.java 类。
管理槽 Portlet 应用程序的设计模式与共享槽 Portlet 应用程序的设计模式类似。不过,管理槽 Portlet 应用程序说明了管理员使用 Credential Vault portlet 如何创建和删除槽。
创建管理槽
1. 以门户管理员身份登录到 WebSphere Portal。
2. 选择 Portal Administration->Security->Credential Vault Portlet。
3. 单击 Add a Vault Slot。
4. 输入下列值:
设置 | 值 |
Vault | Default |
Name | AdminSlot1 |
Vault segment | DefaultAdminSegment |
Vault slot is shared | 不选 |
Vault resource associated with vault slot | 选择 new 并输入“Lotus” |
5. 单击 OK创建一个管理槽。
图 3:创建管理槽
配置槽名称以及设置和获取凭证
管理员用下面的 portlet.xml中的代码将槽名称设置为配置参数。槽名称被作为 vaultSlotName数据成员存储在 portlet 对象中。
<context-param>
<param-name>VaultSlotName</param-name>
<param-value>AdminSlot1</param-value>
</context-param>
portlet 可以用前面几部分中描述的 CredentialAccessService类来设置和检索凭证。下面的代码片段使用 setCredentialSecretUserPassword 方法设置 portlet 代码中的凭证。
vaultService.setCredentialSecretUserPassword(VaultSlotName,
userID, password.toCharArray(), portletRequest);
下面的代码片段使用 getCredential方法获取 portlet 代码中的凭证。
UserPasswordPassiveCredential credential = (UserPasswordPassiveCredential)
vaultService.getCredential(VaultSlotName,"UserPasswordPassive", new HashMap(),
portletRequest);
这两种方法都用 vaultSlotName 作为 slotID 参数。
安装 portlet 及定制页面
1. 安装附带的管理槽 Portlet 应用程序。
2. 定制一个页面时添加了两个 AdministrativeSlotSamplePortlet实例。
3. 针对其中一个 portlet 实例,选择 edit模式并提交用户标识和密码。
那个 portlet 使用管理员创建的槽将凭证存储到凭证保险库中。另一个 portlet 从凭证保险库中抽取用户标识和密码并显示它们。
除了槽是由管理员创建的外,这个 portlet 与共享槽样本 portlet 类似。
图 4:拥有相同的凭证的两个 AdminSlotSamplePortlet实例
系统槽 Portlet 应用程序
系统槽 Portlet 应用程序包括一个 portlet,即 SystemSlotSamplePortlet。该 portlet 代码并不创建保险库槽;相反,它认为使用 Credential Vault portlet 已经创建了一个保险库槽。这个槽名称被设置为 portlet.xml中的具体的portlet 的配置参数。
管理员创建保险库槽时,他选中 Vault slot is shared并指定一个共享的用户标识和代码。选择这个设置表示所有用户共享这个槽。
这个 portlet 应用程序演示了如何创建一个系统槽、配置槽名称以及设置和获取凭证。您可以下载 systemslotsample.war文件并打开它,以便研究这个应用程序的代码。这个 WAR 文件的 WEB-INF\classes\samplepkg目录下包含了 SystemSlotSamplePortlet.java类。
如果一个 portlet 需要用大型机管理员用户标识连接到大型机上,那么系统槽 Portlet 应用程序设计模式尤为合适。与所有用户相关联的 portlet 可以通过从系统槽检索共享的凭证来把它们提交给后端应用程序。
创建系统槽
1. 以门户管理员身份登录。
2. 选择 Portal Administration->Security->Credential Vault Portlet。
3. 单击 Add a Vault Slot。
4. 输入下列值:
设置 | 值 |
Vault | Default |
Name | SystemSlot1 |
Vault slot is shared | check |
Vault Segment | DefaultAdminSegment |
Vault resource associated with vault slot | 选择 new 并输入 MainFrameDB1 |
Shared User ID | dbadmin |
Shared Password | xyzabc |
Confirm Password | xyzabc |
5. 单击 OK创建系统槽。
图 5:创建系统槽
配置槽名称以及设置和获取凭证
portlet 开发者使用下面的 portlet.xml中的代码将槽名称设置为配置参数值。WebSphere Portal 将槽名称存储为 portlet 对象中的 vaultSlotName数据成员。
<context-param>
<param-name>VaultSlotName</param-name>
<param-value>SystemSlot1</param-value>
</context-param>
portlet 可以用前面几部分中描述的 CredentialAccessService 类来设置和检索凭证。
安装 portlet 和定制页面
安装附带的系统槽 Portlet 应用程序,并定制一个页面,在该页面上放置一个 SystemSlotSamplePortlet实例。这个 portlet 窗口显示管理员在创建槽时输入的共享用户标识和密码。
图 6:一个显示已存储的凭证的 SystemSlotSamplePortlet实例
结束语
本文演示了创建保险库槽以及使用缺省保险库存储和检索凭证的各种方式。您可以用样本 portlet 应用程序中的代码片段作为示例嵌入到现有的 portlet 中。通过使用这些技术,您可以启用一个用户单点登录以连接到需要认证的后端应用程序。WebSphere Portal 开发者还可以实现一些保险库适配器,这些保险库适配器用自己的资源库作为保险库,而不是使用缺省保险库。