5. 验证:RP 向 OP 请求用户名验证,并确保通信没有受到干扰。 6. 转到应用程序:身份验证之后,RP 为用户指向其先前请求的资源。 接下来,我们将详细分析这些步骤中的每一步,包括代码例子。在我们逐步查看下面内容时,我将从头到尾使用一个例子来阐述 OpenID 身份验证过程。 获取用户提供的标识符
这是 RP 应用程序的任务。在工作示例中,用户名是在应用程序的
OpenIdRegistrationPage 上获取的。我输入我的 OpenID 并单击 Confirm
OpenID 按钮。示例应用程序(充当 RP)现在知道我的用户提供标识符了。图 1 显示了运行中的示例应用程序的一幅截图。
图 1. 获取用户提供的标识符
在本例中,用户提供的标识符是 redneckyogi.myopenid.com。
UI 代码负责两项工作:确保用户在 Your OpenID 文本框中输入了文本,且在用户单击 Confirm OpenID 按钮时提交窗体。在确认之后,应用程序开始调用序列。清单 1 显示了 OpenIdRegistrationPage 中提交窗格和执行调用序列所用的代码。
清单 1. 使用 RegistrationService.java 执行 OpenID 身份验证调用序列的 Wicket UI 代码
Button confirmOpenIdButton = new Button(\ public void onSubmit() {
String userSuppliedIdentifier = formModel.getOpenId(); DiscoveryInformation discoveryInformation = RegistrationService. performDiscoveryOnUserSuppliedIdentifier( userSuppliedIdentifier);
MakotoOpenIdAwareSession session =
(MakotoOpenIdAwareSession)owningPage.getSession();
session.setDiscoveryInformation(discoveryInformation, true); AuthRequest authRequest =
RegistrationService.createOpenIdAuthRequest( discoveryInformation, returnToUrl); getRequestCycle().setRedirect(false);
getResponse().redirect(authRequest.getDestinationUrl(true)); } };
试着不要受示例及其使用 Wicket UI 代码的方式困扰(不过如果您很好奇,完全可以查看 OpenIdRegistrationPage.java,也就是清单 1 的来源)。这里的重点是,当用户单击按钮时,UI 代码委托 RegistrationService 的各种方法来调用 openid4java 的 API,主要做三项工作(每一项都在清单 1 中用粗体表示): 1. 在用户提供的标识符上执行发现
2. 创建用于生成身份验证请求的 openid4java AuthRequest 对象 3. 重定向浏览器到 OpenID 提供者
重定向浏览器之后,UI 代码完成任务,现在控制权在 OP 手中。注意,
myopenid.com 是标识符的一部分,且用户提供的标识符不是结构良好的 URL。在标识符中仍然需要编码足够的信息,以允许 openid4java 规范化并执行发现。这将在下一部分介绍。 发现(discovery)
RP 采用用户提供的标识符,并将其转化为一种格式,可用于确定两个内容:OpenID 提供者(OP)是谁,如何联系 OP。
RP 使用发现过程来确定如何向 OP 发出请求,而关键便是用户提供的标识符。但是,在将用户提供的标识符用于发现之前,首先必须将其规范化。 openid4java 实际上已经承担了规范化用户提供标识符的工作,所以这里无需再作详细讨论。 两种不同的形式是:
1. XRI:可扩展资源标识符 2. URL:统一资源定位符
本文中我们将看一些 URL 示例。图 1 中的用户提供标识符是一个缺少模式的 URL,因此,作为规范化工作的一部分,openid4java 向其附加 “http://”,从而构成声明的标识符 http://redneckyogi.myopenid.com。
声明的标识符中的编码信息包含 OP 的名称,在本例中是 myOpenID。由于声明的标识符是一个 URL,openid4java 知道如何联系 OP — 在 http://myopenid.com上 — 这正是它所要做的。
清单 2(来自示例应用程序的 RegistrationService 类)显示 RP 如何使用 openid4java 执行发现。
清单 2. 使用 openid4java 执行发现 public static
DiscoveryInformation performDiscoveryOnUserSuppliedIdentifier( String userSuppliedIdentifier) {
DiscoveryInformation ret = null;
ConsumerManager consumerManager = getConsumerManager(); try {
// Perform discover on the User-Supplied Identifier List
consumerManager.discover(userSuppliedIdentifier); // Pass the discoveries to the associate() method... ret = consumerManager.associate(discoveries); } catch (DiscoveryException e) {
String message = \ log.error(message, e);
throw new RuntimeException(message, e); }
return ret; }
openid4java 进行 OpenID 身份验证所用的核心类是 ConsumerManager。openid4java 对于该类的使用有严格的准则。它将该类作为静态类成员存储并通过 getConsumerManager() 方法予以访问(参见示例应用程序中的 RegistrationService.java 了解更多信息)。
openid4java 允许使用一行代码(清单 2 中粗体部分)规范化用户提供的标识符并执行发现。返回的是 DiscoveryInformation 对象的 java.util.List。可将这些对象看作不透明对象。一定要保留这些对象,因为当您的 RP 实现选择构建与 OP 的关联时,要用到它们(如示例应用程序)。 关联
关联是 RP 和 OP 建立共享密钥(通过 Diffie-Hellman 密钥交换)的一种方式,能使它们之间的交互更安全可信。关联不是 OpenID 规范所必需的。关联是从 RP 代码中执行的,仅需调用 ConsumerManager 上的 associate() 方法即可,如清单 3 所示。
清单 3. 使用 openid4java 建立关联
public static
DiscoveryInformation performDiscoveryOnUserSuppliedIdentifier( String userSuppliedIdentifier) {
DiscoveryInformation ret = null;
ConsumerManager consumerManager = getConsumerManager(); try {
// Perform discover on the User-Supplied Identifier List
consumerManager.discover(userSuppliedIdentifier); // Pass the discoveries to the associate() method... ret = consumerManager.associate(discoveries); } catch (DiscoveryException e) {
String message = \ log.error(message, e);
throw new RuntimeException(message, e); }
return ret; }
这种方法返回 DiscoveryInformation 对象,它用来描述发现的结果(您可将该对象看作不透明对象)。示例应用程序存储一个 session 中的
DiscoveryInformation 对象,因为稍后会用到该对象。要发出身份验证请求,就需要该对象,接下来我们将对此进行讨论。 身份验证
RP 在用户提供的标识符上成功执行发现后,该到验证用户身份的时候了。ConsumerManager 需要建立一个称作 AuthRequest 的特殊对象,OP 会使用该对象处理身份验证请求。
在此次交互中,需要利用名为 SimpleRegistration(简称 SReg)的一个 OpenID 扩展;该扩展允许 RP 提出以下请求:在响应中返回 OP 用户资料中的某些属性。清单 4 显示了建立 AuthRequest 对象和使用 SReg 请求属性的代码。
清单 4. 建立 AuthRequest 并使用 SReg 扩展 public static AuthRequest
createOpenIdAuthRequest(DiscoveryInformation discoveryInformation, String returnToUrl) { AuthRequest ret = null; // try {
// Create the AuthRequest object ret =
getConsumerManager().authenticate(discoveryInformation, returnToUrl);
// Create the Simple Registration Request SRegRequest sRegRequest =
SRegRequest.createFetchRequest();
sRegRequest.addAttribute(\ sRegRequest.addAttribute(\ sRegRequest.addAttribute(\
sRegRequest.addAttribute(\ret.addExtension(sRegRequest); } catch (Exception e) {
String message = \ \ log.error(message, e);
throw new RuntimeException(message, e); }
return ret; }