警告:此页面介绍的是 Google 的旧版 API(即 Google Data API),仅适用于Google Data API 目录中列出的 API,其中许多 API 已被更新的 API 取代。如需了解特定新 API,请参阅相应新 API 的文档。如需了解如何使用较新的 API 授权请求,请参阅 Google 账号身份验证和授权。
本文档介绍了如何使用 Google Data API 客户端库连接到 Google 的 AuthSub 网页应用身份验证。
AuthSub 接口允许基于 Web 的应用代表用户访问 Google 服务。为了保持高安全级别,AuthSub 接口可让应用获取身份验证令牌,而无需处理用户的账号登录信息。
Google Data API 客户端库提供了一些方法,可帮助您在 Web 应用中使用 AuthSub。具体来说,有用于构建请求网址、获取一次性身份验证令牌、将一次性令牌换成会话令牌以及对请求进行签名的各种方法。
注意:JavaScript 客户端库有其自己的 AuthSub 版本,称为 AuthSubJS。如需了解如何在 JavaScript 应用中使用 AuthSubJS,请参阅将“AuthSub”身份验证与 JavaScript 客户端库搭配使用。
受众群体
本文档面向希望使用 Google Data API 客户端库让其基于 Web 的应用代表用户访问 Google 服务的程序员。
本文档假定您熟悉 AuthSub 接口以及将 AuthSub 纳入 Web 应用的一般流程。如需详细了解 AuthSub 的协议,请参阅适用于 Web 应用的 AuthSub 身份验证。
在不使用客户端库的情况下使用 AuthSub 和 Google Data API
如果您希望 Web 应用客户端使用 AuthSub 作为身份验证系统与 Google Data 服务互动,那么您真正需要了解的所有信息都在适用于 Web 应用的 AuthSub 身份验证中。 如果您不想使用 Google Data API 客户端库,则无需使用。
以下是应用可能使用 AuthSub 对用户进行身份验证的流程概要:
您的应用会构建相应的 AuthSub 网址,然后将用户发送到该网址,以便用户登录;AuthSub 系统会将用户发送回您网站上指定的网址,并返回一次性令牌;您的应用可以选择将该令牌换成会话令牌;然后,您的应用会在发送给服务的每个请求中,通过“Authorization”标头发送该令牌。
Google Data API 客户端库可为您处理各种详细信息,从而简化此授权流程。本文档将介绍具体操作方法。
使用 AuthSub 和 Google Data API:客户端库示例
本部分展示了一个示例,说明如何使用 Google Data API 客户端库方法来遵循 AuthSub 文档的“使用 AuthSub”部分中概述的步骤。
在此示例中,我们将 AuthSub 接口集成到与 Google 日历互动的 Web 应用中(不过,您无需了解任何有关 Google 日历的信息即可按照此示例操作)。此示例假定 Web 应用托管在 example.com。
确定要使用的令牌类型(session=0 或 session=1)
您可以选择使用一次性令牌 (session=0) 或会话令牌 (session=1)。本文档将使用会话令牌,因为在会发出多个 API 请求的应用中,会话令牌更有用。
如 AuthSub 文档中所述,如果您决定在 Web 应用中使用会话令牌,则需要自行管理令牌存储。本文档不涉及令牌管理。另请注意,使用 session=0 请求的令牌以后无法交换(升级)为长期有效的会话令牌。
决定是否注册 Web 应用(secure=0 或 secure=1)
AuthSub 可在三种不同的模式下使用,即未注册、已注册和已注册并增强了安全性。本文档的其余部分将最后一种方法称为安全 AuthSub。 虽然未注册/已注册模式比安全的 AuthSub 更易于设置,但 Google 建议您使用安全令牌,以提高安全性。
登记方式
选择基于 Web 的应用的注册可为您的应用带来以下好处:
- 更高级别的安全性。
- 获得 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/ 将允许访问日历的所有 Feed:http://www.google.com/calendar/feeds/*。
多范围令牌
如需创建可访问多个 Google Data 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)
构建“下一个”网址后,您的应用可以以多种方式使用该网址,将用户发送到 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 重定向回您的应用时,该令牌会作为查询参数附加到“next”网址中。在上述示例中,用户登录后,Google 会重定向到类似 http://www.example.com/RetrieveToken?token=DQAADKEDE 的网址。您的应用应从其网址查询参数中提取令牌值。
如果您的应用在将用户发送到 AuthSub 系统之前,在用户的浏览器中设置了身份验证 Cookie,那么当 Google 重定向回“next”网址时,您的应用可以读取该身份验证 Cookie,以识别到达该网址的用户。您可以使用此类 Cookie 将应用中的用户 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 anAuthSubTokenorSecureAuthSubTokenobject. # 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 anAuthSubTokenorSecureAuthSubTokenobject. # 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,交换需要您先设置 Zend_Gdata_HttpClient 并使用 setAuthSubPrivateKeyFile() 设置 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 会话令牌不会过期;您的客户端可以根据需要存储会话令牌。
因此,当客户端完成会话令牌的使用后,可以按照 AuthSub 文档中的说明,使用 AuthSubRevokeToken 处理程序撤消令牌。
例如,如果您想以传统会话的方式管理令牌,那么您的客户端可以在用户会话开始时获取令牌,并在用户会话结束时撤消令牌。
如需撤消令牌,请在每个客户端库中使用以下代码:
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()
其他资源和示例
- Google Data API 提示博客上的 AuthSub 示例
- Python 客户端库 AuthSub 示例
- Java 客户端库 AuthSub 示例
- 文章:将 AuthSub 与 .NET 客户端库搭配使用
- 文章:使用 JavaScript 客户端库进行“AuthSub”身份验证
生成自签名私钥和公共证书以用于安全 AuthSub
私钥用于生成签名,该签名必须包含在每个请求中。Google 会使用嵌入在证书中的公钥来验证签名。公钥必须是采用 PEM 格式的 X.509 证书中编码的 1024 位 RSA 密钥。证书应在注册时发送给 Google。
以下部分提供了有关如何使用两种特定工具(即 OpenSSL 实用程序和 Java 的 keytool 实用程序)生成密钥和证书的示例。
这些示例并非专门针对 Google Data 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 Data 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