将 AuthSub 与 .NET 客户端库配合使用

Jeff Fisher,Google 数据 API 团队
2007 年 8 月

简介:为什么 AuthSub 很重要?

Google 数据 API(简称“GData”)的好处在于,它允许开发者构建与 Google 服务交互的应用。更具体地说,它们允许您访问私有用户数据以在您的应用中使用。借助 API,您可以编写应用以同步、导入、导出和以其他方式管理这些数据。虽然这些 API 可为您提供这些强大的功能,但您必须记得以负责任的方式使用它们。由于用户数据属于隐私信息,因此自然希望以安全的方式访问这些数据。其中的关键部分是以安全方式向 Google 的服务器进行身份验证。

假设您有一个很好的新 Web 应用,您希望将其绑定到 Google Web 服务中存储的数据。现在,您需要进行身份验证才能访问这些私有数据。为什么不使用一些简单的代码,例如 StreetView?这种方法行之有效,但您还需处理更多私密数据:用户的登录凭据。SafeFrame 要求您的应用询问用户的 Google 用户名和密码。这适用于在用户的个人计算机上运行的桌面应用,但不太适合基于网络的应用。除了在您自己的服务器上处理这些凭据的责任外,您有些更为谨慎的用户可能会担心您可能会存储其信息。用户的另一个常见顾虑是,他们只想授予某个程序访问特定服务(如 Google 日历中的活动)的权限,但不想授予某些其他服务(例如 Google 文档)的访问权限。AuthSub 解决了这两个问题,它可以让用户通过 Google 服务器进行身份验证,并让您的程序只请求所需的访问权限。

现在,您已经对 AuthSub 背后的理论有了足够的了解,是时候进行一些编码了!在本文中,我选择了保持简单,并在一个 ASP 网页中执行所有操作,但您应该能够轻松将此处演示的技术集成到您自己的应用中。

处理身份验证

那么,在 Web 应用中实际使用 AuthSub 需要什么?首先,从 GData 客户端库进行一些标准导入:

<%@ Import Namespace="Google.GData.Client" %>
<%@ Import Namespace="Google.GData.Extensions" %>
<%@ Import Namespace="System.Net" %>

现在,您要做的第一件事是将用户转到专门制作的网址。这样一来,Google 的服务器就可以处理身份验证,然后将用户重定向回您的网站。幸运的是,您无需手动生成此网址,因为有适合自己的方法。让我们看一个示例:

authSubUrl = AuthSubUtil.getRequestUrl(target, scope, secure, session);
  • target:这是一个包含您的 Web 应用的网址的字符串。系统将在验证身份后将用户重定向到此页面。
  • scope:此字符串取决于您所使用的 API。它对应于 GData API 中的一个 Feed。例如,包含用户的所有日历信息的 Feed 都是“http://www.google.com/calendar/feed/default/private/full”。
  • secure:这是一个布尔值,用于告知服务器您已向 Google 注册,并对您的服务器请求进行加密签名。此参数通常默认为 false,尤其是在测试环境中工作时。
  • session:这是另一个布尔值,表示您想使用“会话令牌”,而不是“一次性使用令牌”。稍后,此参数的作用将变得更加明确。

用户点击生成的网址后,会转到 Google 帐号页面,从而可以登录 Google 帐号。然后,用户会被重定向到您在“target”变量中指定的网页,但查询参数“token”将包含一次性令牌。通常,此令牌只能使用一次。也就是说,它可以用来对指定的 Feed 执行一项操作。不过,如果您将“session”参数指定为 true,就可以用它来交换“会话令牌”,直到用户结束会话为止。您可以通过以下方式执行此操作:

String token = Request.QueryString["token"];
Session["token"] = AuthSubUtil.exchangeForSessionToken(token, null).ToString();

在这里,您可以从查询参数中提取令牌,并将其换成“会话令牌”。然后,您可以选择保存该令牌到 .NET 自动 Session 数组中,以便日后使用。当然,您也可以选择将令牌存储在数据库中。下一步是使用此令牌发出经过身份验证的请求:

GAuthSubRequestFactory authFactory = new GAuthSubRequestFactory("cl", "My-Cool-Application");
authFactory.Token = (String) Session["token"];
CalendarService service = new CalendarService(authFactory.ApplicationName);
service.RequestFactory = authFactory;

您在此处设置了 CalendarService 对象,以使用 AuthSub 与 Google Calendar API 进行交互以进行身份验证。请注意,GAuthSubRequestFactory 的构造函数中使用的“cl”是日历的服务名称。如需查看其他服务名称,请参阅 Google 数据 API 常见问题解答

安全(已注册)AuthSub

如果您选择注册 Web 应用,则可以在使用 AuthSub 时启用额外的安全级别。这样,您就可以对代码发出的所有请求进行数字签名,这样,别人就可以使用您的 AuthSub 令牌(除非拥有您的私钥)。第一步是确保在调用 AuthSubUtil.getRequestUrl 时将“secure”参数设置为 true,以生成正确的 AuthSub 链接。您还需要对代码进行另外两项更改:

String token = Request.QueryString["token"];
Session["token"] = AuthSubUtil.exchangeForSessionToken(token, rsaKey).ToString();

...

authFactory.PrivateKey = rsaKey;

首先,请注意,现在您要将变量“rsaKey”传递给 exchangeForSessionToken 方法,而不是 null。在设置与服务的连接时,此变量还用于设置 GAuthSubRequestFactory 的属性。“rsaKey”变量是您在 Google 注册的 x509 证书的私钥组件所对应的 RSACryptoServiceProvider

生成 RSA 私钥和自签名证书可能会有些令人困惑,尤其是 .NET 框架无法理解以 PEM 格式存储的密钥或证书。以下命令显示了如何使用 OpenSSL 工具套件生成私钥和公共证书:

openssl req -x509 -nodes -days 365 -newkey rsa:1024 -sha1 -subj \
  '/C=US/ST=CA/L=Mountain View/CN=www.example.com' -keyout \
  test_key.pem -out test_cert.pem

openssl pkcs12 -export -in test_cert.pem -inkey test_key.pem \
  -out test_cert.pfx -name "Testing Certificate"

第一步,分别生成 PEM 格式的私钥和公共 X509 证书,这两个证书分别名为“test_key.pem”和“test_cert.pem”。请注意,该证书已设置为注册在美国加利福尼亚州山景城的“www.example.com”。在此处将适当的值替换为贵公司的值。“test_cert.pem”文件包含您需要在 AuthSub 注册页面上提交的信息。

第二步基于您的私钥和证书生成一个 PFX 文件。此文件可以导入 .NET 客户端库,对对 GData API 发出的请求进行数字签名。以下代码展示了如何将 PFX 文件中的私钥导入 Web 应用:

protected AsymmetricAlgorithm getRsaKey()
{

  X509Certificate2 cert = new X509Certificate2("C:/MyAspSite/test_cert.pfx","");
  RSACryptoServiceProvider privateKey = cert.PrivateKey as RSACryptoServiceProvider;

  return privateKey;
}

向 API 进行身份验证时,可以使用此代码段定义的 getRsaKey() 函数代替上面显示的“rsaKey”变量。当然,文件路径应替换为您生成的 PFX 文件的相应位置。

完成代码清单

要展示如何使用上一部分中演示的方法,最简单的方法就是使用真实示例。以下示例代码是一个简单的 ASP 网页,它使用 AuthSub 对用户进行身份验证,然后输出其 Google 日历的活动。

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<%@ Import Namespace="Google.GData.Client" %>
<%@ Import Namespace="Google.GData.Extensions" %>
<%@ Import Namespace="Google.GData.Calendar" %>
<%@ Import Namespace="System.Net" %>

<script runat="server">
    void PrintCalendar() {

        GAuthSubRequestFactory authFactory = new GAuthSubRequestFactory("cl", "TesterApp");
        authFactory.Token = (String) Session["token"];
        CalendarService service = new CalendarService(authFactory.ApplicationName);
        service.RequestFactory = authFactory;

        EventQuery query = new EventQuery();

        query.Uri = new Uri("http://www.google.com/calendar/feeds/default/private/full");

        try
        {
            EventFeed calFeed = service.Query(query);
            foreach (Google.GData.Calendar.EventEntry entry in calFeed.Entries)
            {
                Response.Write("Event: " + entry.Title.Text + "<br/>");
            }
        }
        catch (GDataRequestException gdre)
        {
            HttpWebResponse response = (HttpWebResponse)gdre.Response;
            
            //bad auth token, clear session and refresh the page
            if (response.StatusCode == HttpStatusCode.Unauthorized)
            {
                Session.Clear();
                Response.Redirect(Request.Url.AbsolutePath, true);
            }
            else
            {
                Response.Write("Error processing request: " + gdre.ToString());
            }
        }
    }

</script>


<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Test Site</title>
</head>
<body>

    <form id="form1" runat="server">
    <h1>AuthSub Sample Page</h1>
    <div>
    <%
        GotoAuthSubLink.Visible = false;
        
        if (Session["token"] != null)
        {
            PrintCalendar();
        }
        else if (Request.QueryString["token"] != null)
        {
            String token = Request.QueryString["token"];
            Session["token"] = AuthSubUtil.exchangeForSessionToken(token, null).ToString();
            Response.Redirect(Request.Url.AbsolutePath, true);
        }
        else //no auth data, print link
        {
            GotoAuthSubLink.Text = "Login to your Google Account";
            GotoAuthSubLink.Visible = true;
            GotoAuthSubLink.NavigateUrl = AuthSubUtil.getRequestUrl(Request.Url.ToString(),
                "http://www.google.com/calendar/feeds/",false,true);
        }
        
     %>
    <asp:HyperLink ID="GotoAuthSubLink" runat="server"/>

    </div>
    </form>
</body>
</html>

总结

借助 AuthSub,您的 Web 应用能够以安全且可控的方式访问存储在用户 Google 帐号中的数据。使用 .NET 客户端库,可将基于 ASP 的网站与 Google 服务轻松集成。本文旨在帮助您上手,但建议您参阅其他资源: