J Steven Perry, 首席顾问, Makoto Consulting Group, Inc.
简介: OpenID 是一个分散式身份识别协议,能使用户更易于访问 Java? Web 应用程序中的资源。 在由两部分组成的文章的第 1 部分中,您将了解 OpenID 验证规范 以及在 Java 示例应用程序中加入该规范的步骤。作者 J. Steven Perry 没有手动实现 OpenID 验证规范,而是使用 openid4java 库和一个流行的 OpenID 提供者 myOpenID,为 Wicket 中编写的 Java 应用程序创建安全可靠的注册过程。
OpenID 是一套分散式身份验证系统。通过 OpenID 我可以证明自己拥有类似 http://openid.jstevenperry.com/steve 这样的 URL,而且可以使用经验证的身份登录任何支持 OpenID 的站点 — 比如 Google、Slashdot 或 Wordpress。OpenID 对终端用户来说无疑是个不错的工具。但是对 OpenID 的使用引发我产生这样的想法:“如果使用 OpenID 为我给客户编写的基于 Java 的 Web 应用程序创建标准可靠的身份识别系统,会怎么样呢?”
在这个由两部分组成的文章中,我将向您展示如何使用 openid4java 库和知名的 OpenID 提供者 myOpenID 为基于 Java 的 Web 应用程序创建身份验证系统。还将向您展示如何使用一个 OpenID 简单注册扩展(Simple Registration Extension)(SReg)接收用户信息。
首先我将解释什么是 OpenID 并说明如何获得自己的 OpenID。接下来,简短地介绍 OpenID 身份验证的运作方式。最后,概述使用 openid4java 执行 OpenID 身份验证所需的步骤。在本文第 2 部分,您将了解如何创建自己的 OpenID 提供者。
我将通篇使用基于 Wicket 的 Java Web 应用程序,这是我专门为本文编写的。您可以随时下载应用程序 源代码。另外,您可能希望看一下 openid4java 库(参见 参考资料)。
注意:本文重点介绍面向 Java Web 应用程序的 OpenID,不过 OpenID 在任何软件架构模式中都有效。 OpenID 简介
OpenID 是证明用户拥有标识符的一种规范。现在,仅将标识符 看作惟一标识用户的 String。如果您像我一样,会拥有很多标识符或用户名。我在 Facebook、Twitter 和因特网上的大量其他站点上都有用户名。我经常尝试使用同一个用户名,但是这在我要注册的每个新站点上都不可行。因此,我需要记住所有的用户名及其对应的 Web 站点。这是一件很痛苦的事;我常常会用到 “忘记密码?” 这一提示信息。如果有一种方法可以在所有站点使用同一个标识符,该有多好!
OpenID 恰恰可以解决这个问题。通过 OpenID,我可以声明一个标识符,然后在采用 OpenID 协议的任意 Web 站点上使用它。最新统计(来自 OpenID Web 站点)显示有 50,000 多个网站支持 OpenID,包括 Facebook、Yahoo!、Google 和 Twitter。 OpenID 身份验证
OpenID 身份验证是 OpenID 的核心,它包括三个主要概念:
? ?
OpenID 标识符:一个惟一标识用户的文本字符串。
OpenID 依赖方(RP):一种在线资源(可能是一个 Web 站点,也可以是文件、图像或想要进行访问控制的任何资源),使用 OpenID 识别可以访问它的对象。
OpenID 提供者(OP):一个站点,用户可在该站点声明 OpenID,随后登录并为任意 RP 验证身份。
?
OpenID 基金会 是一个社团,该社团成员关注通过 OpenID 规范推进开源身份管理。
OpenID 如何运作?
假设有用户尝试访问属于 RP Web 站点的资源,且 RP 使用 OpenID。要访问该资源,用户必须以一种能被识别(规范化)为 OpenID 的形式呈现其 OpenID。OpenID 由 OP 的位置编码。然后 RP 采用用户标识符并将用户重定向到 OP,此时 OP 会要求用户证明其 ID 请求。
接下来简要介绍一下 OpenID 规范的每个组成部分及其作用。 OpenID 标识符
OpenID 的核心部分当然是 OpenID 标识符。OpenID 标识符(或简称 “标识符”)是惟一标识用户的可读字符串。没有两个用户拥有相同的 OpenID,这正是 OpenID 发挥作用的关键之处。通过遵循 OpenID 验证规范 2.0 版 的规定,OpenID 依赖方能够解码(或 “规范化”)标识符以弄清如何验证用户身份。在 OpenID 的运作过程中,作为编写代码的开发人员,我们感兴趣的是下面两个标识符:
用户提供的标识符 ? 声明的标识符
?
顾名思义,用户提供的标识符是由用户提供给 RP 的标识符。用户提供的标识符必须被规范化 为声明的标识符,这只是将用户提供的标识符转化为标准形式的一种别出心裁的说法。然后可使用声明的标识符通过一个名为 discovery 的进程定位 OP,之后 OP 验证该用户身份。
OpenID 依赖方(RP)
RP 通常由用户提供的标识符呈现,该标识符被规范化为声明的标识符。用户的浏览器(“用户代理”)将被重定向到 OP,这样用户便可以提供其密码并得到身份验证。
RP 不知道也不关心声明的标识符是如何获得验证的;它只想知道 OP 是否成功地验证了用户身份。如果验证成功,用户代理(也可能是用户的浏览器)会被转发到用户正试图访问的安全资源中。如果用户得不到验证,RP 会拒绝任何访问。 Open ID 提供者(OP)
OP(OpenID 提供者)负责发出标识符并执行用户身份验证。OP 还提供基于 Web 的 OpenID 管理。OP 收集并保留每个用户的以下基本信息:
? ? ? ? ? ?
电子邮箱 全名
出生日期 邮编 国家
第一语言
当要求 OP 验证声明的标识符时,用户的浏览器直接转到登录页面,用户在该页面输入其密码。此时的控制权在于 OP。如果用户成功得到身份验证,OP 会将浏览器转到 RP 指定的位置(在一个特殊的 “return-to” URL 中)。如果用户不能进行身份验证,他可能会收到来自 OP 的消息,指出身份验证失败(至少对于两个流行的 OpenID 提供者 ClaimID 和 myOpenID 来说是这样的)。 成为 OpenID 依赖方
现在我们了解了 OpenID 的主要组成部分,以及它们之间的协作方式。文章的其余部分将重点介绍如何使用开源 openid4java 库编写 OpenID 依赖方(RP)。 使用 OpenID 的第一步就是获取一个标识符。这很简单:只需转到 myOpenID 并单击 SIGN UP FOR AN OPENID 按钮即可。选择一个 OpenID,比如 redneckyogi 或 jstevenperry(顺便提一下,两个都是我的用户名)。登录窗体会告诉您所选用户名是否已存在。如果不存在,系统将指导您输入密码、电子邮箱,并在 JChaptcha 格式的文本框中输入一些文本(您不是一个机器人程序,对吧?)。 稍后,您会收到一封电子邮件,其中含有一个链接。单击链接确认电子邮箱,然后 — 恭喜您!— 您现在拥有自己的 OpenID 了!
当然,随着技术的不断发展,会有更多的 OPenID 提供者可供选择(参见 参考资料 获取完整列表)。
为表明获取一个 OpenID 有多么简单快捷,我在大约 30 分钟内用 myOpenID、Verisign 和 ClaimID 的帐户进行了登录。这个时间段也包括输入详细信息和上传图片所花费的时间。
您可能已经拥有 OpenID
据 OpenId.net统计,Google,Wordpress 和其他流行站点均支持 OpenID。如果您已经在这些站点上注册,那么您可能已经拥有一个 OpenID 了。
例如,如果您有一个 Yahoo! 帐户,但是还希望有一个 OpenID(我就是这样,我之前甚至不知道OpenID 是什么)。登录时您只需使用 Yahoo! ID 即可,Yahoo 是您的 OpenID 提供者。您使用 whatever@yahoo.com 提供基于 Yahoo 的 OpenID,然后 RP 会要求 Yahoo 对您进行身份验证(如果您运行本文附带的示例应用程序,您实际上可以看到这个过程)。 关于示例应用程序
正如我在文章开始所讲的,我使用 openid4java 编写了 Java Web 应用程序来创建简单的 OpenID 依赖方(RP)。这是个简单的应用程序,您可以构建该应用程序(WAR 形式),将其放入 Tomcat,然后从本地机器上运行。示例应用程序集中关注以下几步:
? ? ?
用户在注册页面输入其 OpenID。
应用程序验证标识符(将用户定向到其 OP 以进行登录)
身份验证成功之后,应用程序从 OP 获取用户的个人资料,然后将用户定向到 Save 页面,用户可在此页面审查并保存其个人信息。 Save 页面上显示的信息来自 OP。
?
我使用 Wicket 编写了应用程序,是因为我真的很喜欢 Wicket。我试着尽量减少 Wicket 的 “footprint”,这样在学习编写 OpenID 依赖方时才不易受到扰乱。
示例应用程序的架构分为两个职责范围:
在 Wicket 中编写的用户界面
? OpenID 身份验证 — 使用 openid4java 库
?
当然这两个方面彼此交互,不过我再次尝试减少重复部分使其更易于遵循 OpenID 规范,而不是因 Wicket 的细小部分而受到扰乱。 关于 openid4java 和示例应用程序代码
OpenID 验证规范 很复杂。如果您一直实现规范,您可能在编写自己的实现时觉得很容易。不过我很懒。我不想做工作要求以外的工作以解决手头的问题,这正是 openid4java 发挥作用的地方。openid4java 是 OpenID 验证 规范的一个实现,它使得在编程中使用 OpenID 更简单。
接下来的代码显示 openid4java API 如何调用 RP 以使用 OpenID。您可能会注意到,示例应用程序实际上需要很少的代码来实现这个调用。openid4java 确实简化了您的生活。
为减少示例应用程序中的 Wicket footprint,我分离出一段代码,这段代码将 openid4java 调用到自己的 Java 类内,这个 Java 类称作
RegistrationService(位于 com.makotogroup.sample.model)。针对 openid4java API 的使用,该类包括 5 种方法:
? ?
getReturnToUrl() 在身份验证成功之后返回浏览器指向的 URL。 getConsumerManager() 用于获取主 openid4java API 类的实例。该类处理示例 RP 应用程序执行身份验证所需的所有代码。
performDiscoveryOnUserSuppliedIdentifier() 顾名思义,它处理 discovery 进程中出现的潜在问题。
createOpenIdAuthRequest() 创建身份验证所需的 AuthRequest 构造。 processReturn() 用于处理身份验证请求的结果。
?
? ?
编写 RP
身份验证的目的是要用户证明其身份。这样做可以保护 Web 资源,使其免受恶意访问者的攻击。用户证明了其身份之后,您决定是否要授予其访问资源的权利(不过身份验证不是本文的介绍范围)。
本文的示例应用程序执行一个许多 Web 站点都常用的功能:用户注册。它假定用户能证明其身份从而可以进行注册。这是个简单的前提,不过它表明了与 OP 的典型 “对话” 是如何进行的,且如何使用 openid4java 实现该对话。下面是一些基本步骤:
1. 获取用户提供的标识符:RP 获得用户的 OpenID。
2. 发现:RP 规范化用户提供的标识符,以决定联系哪个 OP 进行身份验证,如何与其联系。 3. 关联:并非必要步骤,不过是我强烈推荐的一步,在该步中,RP 和 OP 建立一个安全通信渠道。 4. 身份验证请求:RP 要求 OP 对用户进行身份验证。