Google 数据协议客户端库中的 AuthSub

警告:本页面介绍的是 Google 的旧版 API,即 Google 数据 API;它仅与 Google 数据 API 目录中列出的许多 API 相关,其中许多 API 已替换为较新的 API。如需了解特定新 API,请参阅新 API 的文档。如需了解如何使用较新的 API 向请求授权,请参阅 Google 帐号身份验证和授权

本文介绍了如何使用 Google Data API 客户端库连接到 Google 的适用于 Web 应用的 AuthSub 身份验证

AuthSub 接口允许基于网络的应用代表用户访问 Google 服务。为保持高级别的安全,AuthSub 接口可让应用程序获取身份验证令牌,而无需处理用户的登录信息。

Google Data API 客户端库提供了各种方法,可帮助您在 Web 应用中使用 AuthSub。具体而言,有些方法可用于构建请求网址、获取单次使用的身份验证令牌、用一次性令牌交换会话令牌以及对请求进行签名。

注意:JavaScript 客户端库有自己的变种 AuthSub,称为 AuthSubJS。如需了解如何在 JavaScript 应用中使用 AuthSubJS,请参阅在 JavaScript 客户端库中使用“AuthSub”身份验证

观众

本文面向的是希望自己的 Web 应用代表用户使用 Google Data API 客户端库访问 Google 服务的编程人员。

本文假定您熟悉 AuthSub 接口以及将 AuthSub 整合到您的 Web 应用中的一般流程。有关 AuthSub 协议的完整说明,请参阅适用于 Web 应用的 AuthSub 身份验证

在不使用客户端库的情况下使用 AuthSub 和 Google Data API

如果您希望 Web 应用客户端使用 AuthSub 作为身份验证系统与 Google 数据服务进行交互,那么您有必要知道的所有知识都在适用于 Web 应用的 AuthSub 身份验证中。如果您不想使用 Google Data API 客户端库,则无需进行此操作。

下文简要介绍了您的应用如何使用 AuthSub 对用户进行身份验证:

您的应用会构建适当的 AuthSub 网址,然后将用户转到该网址,以便他们可以登录;AuthSub 系统会将用户重定向回您指定的网址,并返回一次性令牌;您的应用可以选择将这个令牌交换为会话令牌;然后,您的应用会在向服务发送的每个请求中在授权标头中发送该令牌。

Google Data API 客户端库可为您处理各种详细信息,从而简化此授权流程。本文档介绍了操作方法。

使用 AuthSub 和 Google 数据 API:客户端库示例

本部分介绍了如何使用 Google Data API 客户端库按照 AuthSub 文档中“使用 AuthSub”部分中所述的步骤操作。

在本例中,我们将 AuthSub 接口集成到一个与 Google 日历交互的 Web 应用程序中(尽管您无需了解任何关于 Google 日历的内容也可参考该示例)。该示例假定 Web 应用托管在 example.com 中。

决定要使用的令牌类型(session=0session=1

您可以选择使用一次性令牌 (session=0) 或会话令牌 (session=1)。本文档将使用会话令牌,因为它们在发出多个 API 请求的应用中更有用。 如 AuthSub 文档中所述,如果您决定在 Web 应用中使用会话令牌,则需要自行管理令牌存储。本文档不涉及令牌管理。另请注意,使用 session=0 请求的令牌以后不能交换(升级)到长期会话令牌。

决定是否注册您的 Web 应用(secure=0secure=1

AuthSub 有三种不同的模式可供使用:未注册已注册已注册,且具有增强的安全性。本文档的其余部分将最后一项称为安全的 AuthSub。虽然未注册/已注册模式比安全 AuthSub 的设置更简单,但 Google 建议您使用安全令牌来增强安全性。

如何注册

选择注册基于网络的应用可为您的应用带来以下好处:

  1. 更高的安全性。
  2. 受 Google 信任(Google 授权页面上不会向用户显示任何警告)。

已注册 + 安全 AuthSub

如果您决定使用安全的 AuthSub,除了注册 Web 应用外,还需要创建自签名 RSA 私钥和公钥对。 有关创建 X.509 证书的示例,请参阅生成用于注册模式的密钥和证书(见下文)。

确定数据访问的范围

每个 Google 服务都会定义一个 scope 值,用于确定(可能会缩小)令牌对用户数据的访问权限。如需查看可用 scope 值的列表,请参阅常见问题解答

由于我们决定与 Google Calendar API 进行交互,因此 scope 应为 http://www.google.com/calendar/feeds/

注意:请务必将范围值设置为尽可能宽泛的网址,除非您需要更精细的限制。例如,如果缩小范围(例如 scope=http://www.google.com/calendar/feeds/default/allcalendars/full),则将仅允许令牌访问 allcalendars/full Feed。使用 scope=http://www.google.com/calendar/feeds/ 将允许访问 Google 日历的所有 Feed:http://www.google.com/calendar/feed/*

多作用域令牌

如需创建可访问多个 Google 数据 API 的令牌,请使用网址编码空间分隔每个范围。以下示例创建了一个令牌,该令牌将同时访问用户的 Google 通讯录和 Google 日历数据。

scope=http://www.google.com/calendar/feeds/%20http://www.google.com/m8/feeds/

请求一次性身份验证令牌

要为给定用户和指定服务获取 AuthSub 令牌,您的应用必须将用户重定向到 AuthSubRequest 网址,从而提示用户登录其 Google 帐号。 (如需详细了解 AuthSubRequest 网址,请参阅适用于 Web 应用的 AuthSub 身份验证。)

如需在应用中构建 AuthSubRequest 网址,请对每个客户端库使用以下命令:

Java

import com.google.gdata.client.*;

String nextUrl = "http://www.example.com/RetrieveToken.jsp";
String scope = "http://www.google.com/calendar/feeds/";
boolean secure = false;  // set secure=true to request secure AuthSub tokens
boolean session = true;
String authSubUrl = AuthSubUtil.getRequestUrl(nextUrl, scope, secure, session);

如果您想对 G Suite 网域中的用户进行身份验证,请按以下步骤操作:

import com.google.gdata.client.*;

String hostedDomain = "example.com";
String nextUrl = "http://www.example.com/RetrieveToken.jsp";
String scope = "http://www.google.com/calendar/feeds/";
boolean secure = false;  // set secure=true to request AuthSub tokens
boolean session = true;
String authSubUrl = AuthSubUtil.getRequestUrl(hostedDomain, nextUrl, scope, secure, session);

.NET

using Google.GData.Client;

String nextUrl = "http://www.example.com/RetrieveToken.aspx";
String scope = "http://www.google.com/calendar/feeds/";
bool secure = false; // set secure=true to request secure AuthSub tokens
bool session = true;
String authSubUrl = AuthSubUtil.getRequestUrl(nextUrl, scope, secure, session);

如果您想对 G Suite 网域中的用户进行身份验证,请按以下步骤操作:

using Google.GData.Client;

String hostedDomain = "example.com";
String nextUrl = "http://www.example.com/RetrieveToken.aspx";
String scope = "http://www.google.com/calendar/feeds/";
bool secure = false; // set secure=true to request secure AuthSub tokens
bool session = true;
String authSubUrl = AuthSubUtil.getRequestUrl(hostedDomain, nextUrl, scope, secure, session);

PHP

require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata_AuthSub');

$nextUrl = 'http://www.example.com/RetrieveToken.php';
$scope = 'http://www.google.com/calendar/feeds/';
$secure = 0;  // set $secure=1 to request secure AuthSub tokens
$session = 1;
$authSubUrl = Zend_Gdata_AuthSub::getAuthSubTokenUri($nextUrl, $scope, $secure, $session);

如果您想对 G Suite 网域中的用户进行身份验证,请按以下步骤操作:

require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata_AuthSub');

$hostedDomain = 'example.com';
$nextUrl = 'http://www.example.com/RetrieveToken.php';
$scope = 'http://www.google.com/calendar/feeds/';
$secure = 0;  // set $secure=1 to request secure AuthSub tokens
$session = 1;
$authSubUrl = Zend_Gdata_AuthSub::getAuthSubTokenUri($nextUrl, $scope, $secure, $session) . '&hd=' . $hostedDomain;

Python

import gdata.auth

next = 'http://www.example.com/RetrieveToken.pyc'
scope = 'http://www.google.com/calendar/feeds/'
secure = False  # set secure=True to request secure AuthSub tokens
session = True
auth_sub_url = gdata.auth.GenerateAuthSubRequestUrl(next, scope, secure=secure, session=session)

如果您想对 G Suite 网域中的用户进行身份验证,请按以下步骤操作:

import gdata.auth

hosted_domain = 'example.com'
next = 'http://www.example.com/RetrieveToken.pyc'
scope = 'http://www.google.com/calendar/feeds/'
secure = False  # set secure=True to request secure AuthSub tokens
session = True
auth_sub_url = gdata.auth.GenerateAuthSubRequestUrl(next, scope, secure=secure, session=session, domain=hosted_domain)

构建“next”网址后,您的应用可以通过多种方式将用户引导至 AuthSubRequest 处理程序。最常见的方法是,显示一个页面,告知用户他们需要访问链接来授权您的应用访问其 Google 帐号;然后将请求网址附加到该链接。例如,您可以在 Web 应用中输出以下字符串:

String authorizationUrl =
        "<p>MyApp needs access to your Google Calendar account to read your Calendar feed. " +
        "To authorize MyApp to access your account, <a href=\"" + authSubUrl + "\">log in to your account</a>.</p>";

用户点击指向 Google 中 AuthSub 页面的链接,然后登录。然后,AuthSub 系统会使用您提供的“next”网址将用户重定向回您的应用。

提取一次性令牌

当 Google 重定向回您的应用时,该令牌将作为查询参数附加到“下一个”网址。在上述示例中,用户登录后,Google 会重定向到类似 http://www.example.com/RetrieveToken?token=DQAADKEDE 的网址。您的应用应从其网址查询参数中提取令牌值。

如果您的应用在将用户身份验证凭据发送到 AuthSub 系统之前,先在用户的浏览器中设置了身份验证 Cookie,那么当 Google 重定向回“下一个”网址时,您的应用可以读取该身份验证 Cookie 以识别哪个用户达到了该网址。您可以使用此类 Cookie 将应用中的 User-ID 与从 Google 检索到的 AuthSub 令牌相关联。

客户端库为提取一次性令牌提供了便捷方法:

Java

String singleUseToken = AuthSubUtil.getTokenFromReply(httpServletRequest.getQueryString());

.NET

String singleUseToken = Request.QueryString["token"];
// or
String singleUseToken = AuthSubUtil.getTokenFromReply(new Uri(Request.QueryString));

PHP

$singleUseToken = $_GET['token'];

Python

current_url = 'http://' + req.hostname + req.unparsed_uri
# Unlike the other calls, extract_auth_sub_token_from_url() will create an AuthSubToken or SecureAuthSubToken object.
# Use str(single_use_token) to return the token's string value.
single_use_token = gdata.auth.extract_auth_sub_token_from_url(current_url)

如果您使用的是安全的 AuthSub,请务必设置 RSA 私钥,以便创建 SecureAuthSubToken

f = open('/path/to/yourRSAPrivateKey.pem')
rsa_key = f.read()
f.close()
current_url = 'http://' + req.hostname + req.unparsed_uri
# Unlike the other calls, extract_auth_sub_token_from_url() will create an AuthSubToken or SecureAuthSubToken object.
# Use str(single_use_token) to return the token's string value.
single_use_token = gdata.auth.extract_auth_sub_token_from_url(current_url, rsa_key=rsa_key)

请求会话令牌

您从该网址检索的令牌始终是一次性令牌。下一步是使用 AuthSubSessionToken 网址为长会话令牌升级该令牌,如完整的适用于 Web 应用的 AuthSub 身份验证文档中所述。如果您使用的是安全的 AuthSub,则需要在交换之前设置 RSA 私钥。以下是使用各个客户端库的一些示例:

Java

import com.google.gdata.client.*;
import com.google.gdata.client.calendar.*;

String sessionToken = AuthSubUtil.exchangeForSessionToken(singleUseToken, null);

CalendarService calendarService = new CalendarService("google-ExampleApp-v1.0");
calendarService.setAuthSubToken(sessionToken, null);

// ready to interact with Calendar feeds

对于安全的 AuthSub,请将 RSA 私钥传递给 exchangeForSessionToken,而不是粘贴 null

import com.google.gdata.client.*;
import com.google.gdata.client.calendar.*;

java.security.PrivateKey privateKey =
    AuthSubUtil.getPrivateKeyFromKeystore("AuthSubExample.jks", "privKeyPa$$word", "AuthSubExample", "privKeyPa$$word");
String sessionToken = AuthSubUtil.exchangeForSessionToken(singleUseToken, privateKey);

CalendarService calendarService = new CalendarService("google-ExampleApp-v1.0");
calendarService.setAuthSubToken(sessionToken, privateKey);

// ready to interact with Calendar feeds

.NET

using Google.GData.Client;
using Google.GData.Calendar;

String sessionToken = AuthSubUtil.exchangeForSessionToken(singleUseToken, null).ToString();

GAuthSubRequestFactory authFactory = new GAuthSubRequestFactory("cl", "google-ExampleApp-v1.0");
authFactory.Token = (String) sessionToken;

CalendarService calendarService = new CalendarService(authFactory.ApplicationName);
calendarService.RequestFactory = authFactory;

// ready to interact with Calendar feeds

对于安全的 AuthSub,请将 RSA 私钥传递给 exchangeForSessionToken,而不是粘贴 null

using Google.GData.Client;
using Google.GData.Calendar;

protected AsymmetricAlgorithm getRsaKey()
{
  X509Certificate2 cert = new X509Certificate2("C:/MyAspSite/test_cert.pfx", "privKeyPa$$word");
  RSACryptoServiceProvider privateKey = cert.PrivateKey as RSACryptoServiceProvider;
  return privateKey;
}

AsymmetricAlgorithm rsaKey = getRsaKey();
String sessionToken = AuthSubUtil.exchangeForSessionToken(singleUseToken, rsaKey).ToString();

GAuthSubRequestFactory authFactory = new GAuthSubRequestFactory("cl", "google-ExampleApp-v1.0");
authFactory.Token = (String) sessionToken;
authFactory.PrivateKey = rsaKey;

CalendarService calendarService = new CalendarService(authFactory.ApplicationName);
calendarService.RequestFactory = authFactory;

// ready to interact with Calendar feeds

PHP

require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata_AuthSub');
Zend_Loader::loadClass('Zend_Gdata_Calendar');

$sessionToken = Zend_Gdata_AuthSub::getAuthSubSessionToken($singleUseToken);

// Create a Calendar service object and set the session token for subsequent requests
$calendarService = new Zend_Gdata_Calendar(null, 'google-ExampleApp-v1.0');
$calendarService->setAuthSubToken($sessionToken);

// ready to interact with Calendar feeds

对于安全的 AuthSub,广告交易平台要求您先使用 setAuthSubPrivateKeyFile() 设置 Zend_Gdata_HttpClient 并设置 RSA 私钥:

require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata_AuthSub');
Zend_Loader::loadClass('Zend_Gdata_Calendar');

$client = new Zend_Gdata_HttpClient();
$client->setAuthSubPrivateKeyFile('/path/to/myrsakey.pem', null, true);
$sessionToken = Zend_Gdata_AuthSub::getAuthSubSessionToken($singleUseToken, $client);

$calendarService = new Zend_Gdata_Calendar($client, 'google-ExampleApp-v1.0');
$calendarService->setAuthSubToken($sessionToken);

// ready to interact with Calendar feeds

Python

import gdata.calendar
import gdata.calendar.service

calendar_service = gdata.calendar.service.CalendarService()
calendar_service.UpgradeToSessionToken(single_use_token)  # calls gdata.service.SetAuthSubToken() for you

# ready to interact with Calendar feeds

注意:只要您使用 gdata.auth.extract_auth_sub_token_from_url(url, rsa_key=rsa_key) 提取一次性令牌,过程就适用于安全的 AuthSub。

注意:使用安全的 AuthSub 时,您的私钥本身不会通过网络发送。客户端库发送使用密钥对请求签名生成的唯一签名,而不是密钥本身。

使用会话令牌

您可以使用会话令牌对向服务器发出的请求进行身份验证,方法是将令牌放入 Authorization 标头中,如 AuthSub 文档中所述。

设置会话令牌后,您可以使用标准 Google Data API 客户端库调用来与服务进行交互,而无需考虑令牌。如需了解详情,请参阅客户端库文档和 Google Data API 开发者指南,以了解您所使用的服务和语言。

检索会话令牌的相关信息

如果要测试您的客户端和服务器是否同意令牌的参数,可以将令牌传递给 AuthSubTokenInfo 处理程序,后者会返回一组包含令牌相关信息的名称值对。

Java

Map<String, String> tokenInfo = AuthSubUtil.getTokenInfo(sessionToken, null);

如果您使用的是安全的 AuthSub,请传入您的 RSA 私钥:

Map<String, String> tokenInfo = AuthSubUtil.getTokenInfo(sessionToken, privateKey);

.NET

Dictionary<String, String> tokenInfo = AuthSubUtil.GetTokenInfo(sessionToken, null);

如果您使用的是安全的 AuthSub,请传入您的 RSA 私钥:

Dictionary<String, String> tokenInfo = AuthSubUtil.GetTokenInfo(sessionToken, privateKey);

PHP

$tokenInfo = Zend_Gdata_AuthSub::getAuthSubTokenInfo($sessionToken);

如果您使用的是安全的 AuthSub,请传入 Zend_Gdata_HttpClient,以便使用 RSA 私钥为请求签名:

$tokenInfo = Zend_Gdata_AuthSub::getAuthSubTokenInfo($sessionToken, $client);

Python

token_info = calendar_service.AuthSubTokenInfo()

撤消会话令牌

AuthSub 会话令牌不会过期;您的客户端可以根据需要保存会话令牌。

因此,当客户端使用会话令牌后,它可以使用 AuthSubRevokeToken 处理程序撤消令牌,如 AuthSub 文档中所述。

例如,如果您希望以类似于传统会话的方式管理令牌,那么客户端可以在用户会话开始时获取令牌,并在用户会话结束时撤消。

要撤消令牌,请在每个客户端库中使用以下代码:

Java

AuthSubUtil.revokeToken(sessionToken, null);

如果您使用的是安全的 AuthSub,请传入您的 RSA 私钥:

AuthSubUtil.revokeToken(sessionToken, privateKey);

.NET

AuthSubUtil.revokeToken(sessionToken, null);

如果您使用的是安全的 AuthSub,请传入您的 RSA 私钥:

AuthSubUtil.revokeToken(sessionToken, privateKey);

PHP

$wasRevoked = Zend_Gdata_AuthSub::AuthSubRevokeToken($sessionToken);

如果您使用的是安全的 AuthSub,请传入 Zend_Gdata_HttpClient,以便使用 RSA 私钥为请求签名:

$wasRevoked = Zend_Gdata_AuthSub::AuthSubRevokeToken($sessionToken, $client);

Python

calendar_service.RevokeAuthSubToken()

其他资源和示例

返回页首

生成用于安全 AuthSub 的自签名私钥和公共证书

私钥用于生成签名,该签名必须包含在每个请求中。Google 会使用证书中嵌入的公钥来验证签名。公钥必须是采用 PEM 格式的 X.509 证书编码的 1024 位 RSA 密钥。证书应在报名时发送给 Google。

以下部分提供了如何使用两个特定工具(OpenSSL 实用程序和 Java 的 keytool 实用程序)生成密钥和证书的示例。

这些示例并非专门针对 Google 数据 API,可出于任何目的使用相同的实用程序生成密钥。

这些示例假设您的公司名为 My_Company,位于美国加利福尼亚州山景城,域名为 example.com。

使用 OpenSSL 生成密钥

如需创建一对 RSA 密钥和相应的证书,您可以使用以下命令:

# Generate the RSA keys and certificate
openssl req -x509 -nodes -days 365 -newkey rsa:1024 -sha1 -subj \
  '/C=US/ST=CA/L=Mountain View/CN=www.example.com' -keyout \
  myrsakey.pem -out /tmp/myrsacert.pem

警告:添加 -nodes 参数会创建一个没有密码的私钥来保护它。不过,您应该考虑省略此参数,以提高安全性。

-sha1 参数指定相应密钥将用于生成 SHA1 签名。

-subj 参数指定证书代表的应用的身份。

-keyout 参数指定将包含密钥的文件。此文件包含敏感信息,应受到保护,不得与任何人共享。

-out 参数指定将包含 PEM 格式证书的文件(可在注册时发送给 Google)。

为 .NET 客户端生成密钥

.NET 框架不理解以 PEM 格式存储的密钥或证书。因此,在创建 .pem 文件后,您需要执行一个额外的步骤:

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

此步骤会根据您的私钥和证书生成一个 PFX 文件。此文件可以导入 .NET 客户端库,以对向 Google 数据 API 发出的请求进行数字签名。

为 Java 客户端生成密钥

Java 客户端接受 PKCS#8 格式的私钥。按照上述说明生成密钥/证书后,根据生成的 .pem 文件创建 .pk8 文件:

openssl pkcs8 -in myrsakey.pem -topk8 -nocrypt -out myrsakey.pk8

或者,您也可以使用 Java 密钥库和 keytool 实用程序创建一对 RSA 密钥和相应的证书。使用以下命令:

# Generate the RSA keys and certificate
keytool -genkey -v -alias Example -keystore ./Example.jks\
  -keyalg RSA -sigalg SHA1withRSA\
  -dname "CN=www.example.com, OU=Engineering, O=My_Company, L=Mountain  View, ST=CA, C=US"\
  -storepass changeme -keypass changeme

警告:“changeme”不是一个好密码,这只是一个示例。

-dname 参数指定证书代表的应用的身份。-storepass 参数用于指定保护密钥库的密码。-keypass 参数用于指定保护私钥的密码。

如需将证书写入可在 ManageDomains 工具中使用的文件,请使用以下命令:

# Output the public certificate to a file
keytool -export -rfc -keystore ./Example.jks -storepass changeme \
  -alias Example -file mycert.pem

返回页首