http://msdn.microsoft.com/zh-cn/magazine/dn198238.aspx
在我看来,需要对用户进行身份验证的大多数网站都将使用社交身份验证网关。 在此背景下,社交身份验证网关仅指由流行社交网络(如 Twitter 和 Facebook)对外公开的身份验证平台。 如果您回想一下早期的 ASP.NET,您肯定会留意到 Passport 计划背后的构想与当今社交身份验证网关背后的构想有某些相似之处。 它们当然不是同一事物,但二者要实现的目标相同:通过减少用户必须跟踪的凭据数量,使其更易于使用。
使用同一套凭据既有优点也有缺点。 一方面,这样做会导致您隐私的安全屏障有所降低。 由于始终使用相同的用户名和密码,黑客会有更多的时间来猜测您的私密信息。同时,一旦您遭到黑客攻击,会暴露更多信息。 不过,另一方面,反复使用相同的凭据会让您连接和试用新服务容易得多。 对于服务所有者而言,这是个好消息。因为这会使其用户数迅速增长,也就意味着该服务获得成功的几率大大增加。
简言之,当今越来越多致力于吸引庞大用户群的网站为用户提供了双重注册选择:通过选择您自己的凭据或通过社交网络身份验证网关。 这一趋势很有吸引力,以致于 ASP.NET MVC 4 的最新版本特别提供了一种针对性的框架,支持用户通过众多社交网络进行身份验证。 本文中,我将介绍 ASP.NET MVC 4 项目模板所提供的代码。 社交身份验证网关通常使用开放式身份验证 (OAuth) 协议,这个协议相当繁琐。 所以,相应的代码并不简单,可能需要进行进一步解释。
进行 MVC 身份验证的最快方式
借助 ASP.NET MVC,您可以在已包含身份验证的代码模板基础上开始编码。 ASP.NET MVC 4 提供的 Internet 应用程序模板扩展了对社交网关的支持。 现在,我们假设您要着手开始一个全新的 Internet 应用程序项目。 在代码的第一个版本,一切中规中矩。 图 1 所在页面提示您尚未启用任何外部身份验证服务。
图 1 未配置任何外部身份验证服务时的 ASP.NET MVC 4 模板
您可以通过对 global.asax 代码隐藏文件稍加修改,启用任意受支持的服务。 对于 ASP.NET MVC 4,建议所创建的项目应包含一个 App_Start 文件夹,其中放置用于执行初始化任务的 xxxConfig 类。 这些类不过是静态方法的容器,用于更好地组织应用程序的启动代码。 以下是一个快照:
- protected void Application_Start()
- { ... AuthConfig.RegisterAuth();}
借助 RegisterAuth 方法所包含的代码,用户可利用他们在其他站点(如 Facebook 和 Twitter)的帐户登录:
- public static class AuthConfig
- {
- public static void RegisterAuth()
- {
- OAuthWebSecurity.RegisterTwitterClient(
- consumerKey: yourTwitterAppKey
- consumerSecret: yourTwitterAppSecret);
- OAuthWebSecurity.RegisterFacebookClient(
- appId: yourFacebookAppKey,
- appSecret: yourFacebookAppSecret);
- }
- }
OAuthWebSecurity 类来自网页框架,位于 Microsoft.Web.WebPages.OAuth 命名空间中。 该类封装了 OAuth 协议的核心功能,由 DotNetOpenAuth (DNOA) 库(请参见 dotnetopenauth.net)实现。
为了通过社交网络对用户进行身份验证,您首先需要在社交网络中创建一款应用程序。 因此,要在您自己的站点内通过 Twitter 和 Facebook 对用户进行身份验证,您需要一款 Twitter 应用程序和一款 Facebook 应用程序。 最可能出现的情况是,您将创建一款与您的站点同名的 Twitter/Facebook 应用程序,并将其配置为链接回网站。 这里提到的 Twitter/Facebook 应用程序并非一款真正的成熟应用程序。此外,它还包含一个用于以编程方式访问 Twitter、Facebook 和其他社交网络的特殊开发人员令牌。 在本专栏专门介绍 Facebook 编程的前几篇文章中,我非常深入地介绍了这一方面。 本文中提到的 Twitter/Facebook 应用程序由一对字符串组成 - 一个称为密钥,另一个称为秘密。 该密钥与秘密与社交应用程序唯一关联。 您可以通过向您计划支持的每个社交网络传递该密钥和秘密,对 OAuthWebSecurity 进行初始化。
您只需调用 RegisterTwitterClient 和 RegisterFacebookClient,即可改变示例项目的 UI,将这些注册选项显示为按钮。 如果用户单击“登录”按钮,系统即将其重定向至 Twitter/Facebook 站点,继续对其进行身份验证。 如果一切顺利,系统随后会将她重定向至原始站点,ASP.NET 会将其识别为一名通过身份验证的用户。
听起来非常简单,对不对? 不过,底层掩藏着许多实质性的细节。
采用 OAuth 路由进行身份验证
当用户单击“Twitter”按钮时,站点会导航至“帐户/外部登录”。 下面,让我们看看该方法的代码(代码位于 AccountController.cs 文件中):
- public ActionResult ExternalLogin(String provider,
- String returnUrl)
- {
- return new ExternalLoginResult(provider,
- Url.Action("ExternalLoginCallback",
- new { ReturnUrl = returnUrl }));
- }
ExternalLoginResult 类是以下代码的一种包装,该代码真正负责联系身份验证网关:
- OAuthWebSecurity.RequestAuthentication(Provider, ReturnUrl);
ExternalLoginResult 类是一个帮助程序类,也位于 AccountController.cs 文件中。 您应该注意到,在项目模板代码中,提供商的名称可通过查看按钮的 name 属性进行解析:
- <button type="submit"
- name="provider"
- value="@p.AuthenticationClient.ProviderName"
- title="Log in using your @p.DisplayName account">
- @p.DisplayName
- </button>
最终,RequestAuthentication 方法会收到身份验证提供商(Twitter、Facebook 或其他任意受支持的提供商)的名称和返回 URL。 默认情况下,OAuthWebSecurity 支持以下提供商:Twitter、Facebook、LinkedIn、Google、Microsoft 和 Yahoo。 在内部,该方法使用 DNOA 库来执行任务。 下面,让我们看看当用户选择通过 Twitter 进行身份验证时会发生什么。
第一步,默认的 Twitter 身份验证页面会呈现在用户面前。 该页面包含执行任务的 Twitter 应用程序的名称—在图 2 所示的示例中为 tFun。 配置 Twitter 应用程序时,您还需要指出用户在登录时需要授予应用程序的权限。 通过恰当配置社交应用程序,可授予 ASP.NET 网站跟踪新用户或为已连接用户发布信息的权限。 对于示例应用程序,情况则不是这样。
图 2 通过 Twitter 进行身份验证
如果用户输入通过 Twitter(或所选的社交网站)验证的有效凭据,Twitter 站点会将用户重定向至所提供的返回 URL。 另一个可重获身份验证控制权的方法是 ExternalLoginCallback。 此时,您只知道尝试访问您的应用程序的用户已被成功识别为一名 Twitter 用户。 您不知道该用户的任何信息,甚至其用户名。 我几乎想象不出一款需要用户通过身份验证但不需要用户名或电子邮件地址的应用程序。 从身份验证步骤返回,应用程序仅收到一个编码,但尚未获得授权以编程方式访问 Twitter API。 要实现此目的,必须将此阶段收到的编码换成一个访问令牌(为预防误用,通常有时间限制)。 这就是在 ExternalLoginCallback 内调用 VerifyAuthentication 方法的目的所在。 VerifyAuthentication 返回的 AuthenticationResult 对象会返回一些有关该用户的信息。 根据具体提供商的不同,您获得的实际信息可能略有不同。不过,该信息通常至少包含用户名。
从身份验证到成员资格
对用户进行身份验证仅仅是第一步。 接下来,您需要在站点内通过用户名跟踪用户。 在传统的 ASP.NET 成员身份系统中,您首先要显示一个登录表单,验证凭据,然后创建一个包含用户名和其他重要信息(可选)的身份验证 cookie。 Twitter 或 Facebook 可为您免去一系列的麻烦:设计登录表单、验证凭据,以及更重要的工作,如存储和管理包含敏感信息(如密码)的帐户。
不过,关键问题是几乎任何需要对用户进行身份验证的应用程序还需要一个按用户名对每个普通用户进行跟踪的成员身份系统。 构建这样一个系统仍然是您需要完成的工作。 这时,ASP.NET MVC 4 模板就可派上用场了。具体做法是额外增加一个步骤,自动提示用户输入显示名称,该名称随后被保存至本地成员表。 这一步骤仅在用户第一次登录至指定站点时需要。 换句话说,图 3 所示的表单用于注册和第一次登录。
图 3 第一次登录和完成站点注册
此阶段输入的名称用于创建 ASP.NET 身份验证 cookie,从而完成整个过程。 您已经使用 Twitter 检查凭据,请用户输入其显示名称并创建常规身份验证 cookie。 从现在起,对于需要经过身份验证的站点,ASP.NET 中的一切和平常一样运行。
默认的 ASP.NET MVC 4 项目模板会将用户数据保存至一个创建于 App_Data 文件夹之下的 .mdf 本地数据库。 该表使用自网页框架继承而来的简单成员 API 进行管理。
以下代码显示了示例项目模板如何获得图 3 所示页面的显示名称:
- var loginData = OAuthWebSecurity.SerializeProviderUserId(
- result.Provider, result.ProviderUserId);
- var name = OAuthWebSecurity
- .GetOAuthClientData(result.Provider)
- .DisplayName;
- return View("ExternalLoginConfirmation",
- new RegisterExternalLoginModel {
- UserName = result.UserName,
- ExternalLoginData = loginData
- });
通过调用 GetOAuthClientData,可访问 Twitter 提供商分享的有关登录用户的任意信息。 通过总结以下代码,可以发现方法 ExternalLoginConfirmation 完成了两项关键工作:
- OAuthWebSecurity.CreateOrUpdateAccount(provider, providerUserId, model.UserName);
- OAuthWebSecurity.Login(provider, providerUserId, createPersistentCookie: false);
第一行在应用程序的成员本地数据库中创建新记录。 第二行实际创建了身份验证 cookie。 默认模板支持大量数据库表,如 UserProfiles 和 webPages_OAuthMembership。 后一张表存储了一条记录,其中包含提供商的名称(即 Twitter)、用户对于该提供商的唯一 ID,以及指向在 UserProfiles 表(包含用户自己在图 3所示的页面中选择的显示名称)中唯一识别用户的内部 ID 的指针。
最终注意事项
OAuth 协议管理提供程序和客户端应用程序之间的交互。 Twitter、Facebook 和其他一些流行社交网络通过 OAuth 公开其 API。 客户端应用程序可以将 Twitter API 用作以下两个主要目的:普通用户身份验证和代表同意的用户对提供程序执行操作。 在以上两种情况下,客户端应用程序都必须登录提供程序并获得一个访问令牌。 访问令牌只能在有限的时间内使用(但是能够以编程方式进行刷新),且仅拥有执行最终用户在输入凭据(请参见图 2)时批准的操作的权限。 应用程序在持有访问令牌时可执行的操作取决于应用程序的需求。该令牌可用于检索,比如说电子邮件地址,并将其存储在特定于应用程序的成员系统中。 该令牌还可用于代表用户发布信息。 即使在该用户与 Twitter 断开连接的情况下,ASP.NET 身份验证 cookie 仍然有效。 不过,在该用户与 Twitter 断开连接的情况下,应用程序不能发布信息。
ASP.NET MVC API,尤其是 OAuthWebSecurity 类提供了一种出色的身份验证方案,但是,它除了能够获得显示名称之外,无法处理与社交提供程序的交互。 它还能够与网页的简单成员提供程序紧密集成,以便将 Twitter 和 Facebook 用户名存储在本地。
Dino Esposito 是《Architecting Mobile Solutions for the Enterprise》(Microsoft Press,2012 年)和《Programming ASP.NET MVC 3》(Microsoft Press,2011 年)的作者,同时也是《Microsoft .NET:Architecting Applications for the Enterprise》(Microsoft Press,2008 年)的合著者。Esposito 定居于意大利,经常在世界各地的业内活动中发表演讲。有关他的情况,请访问 Twitter twitter.com/despos。
衷心感谢以下技术专家对本文的审阅:Mani Subramanian (Microsoft)
Mani Subramanian 过去 12 年来一直从事软件项目的开发和测试工作,研究领域偏向于 SOA、云计算和 core.net。