Google OAuth 2.0 系统支持服务器到服务器的互动,例如 Web 应用和 Google 服务之间的互动。在这种情况下,您需要一个服务帐号,该帐号属于您的应用而不是个别最终用户。您的应用会代表服务帐号调用 Google API,因此用户不会直接参与。此方案有时称为“两方模式的 OAuth”或“2LO”。(相关术语“三方模式 OAuth”是指您的应用代表最终用户调用 Google API,并且有时需要征得用户同意。)
通常,当应用使用 Google API 处理自己的数据而不是用户数据时,应用会使用服务帐号。例如,使用 Google Cloud Datastore 实现数据持久性的应用将使用服务帐号对对 Google Cloud Datastore API 的调用进行身份验证。
Google Workspace 网域管理员还可以向整个网域授予服务帐号,以代表网域中的用户访问用户数据。
本文档介绍了应用如何使用 Google API 客户端库(推荐)或 HTTP 完成服务器到服务器的 OAuth 2.0 流程。
概览
如需支持服务器到服务器交互,请先在 中为您的项目创建服务帐号。如果您想访问 Google Workspace 帐号中用户的用户数据,则应将服务帐号的访问权限委托给全网域帐号。
然后,您的应用准备使用服务帐号的凭据向 OAuth 2.0 身份验证服务器请求访问令牌,从而进行已获授权的 API 调用。
最后,您的应用可以使用访问令牌来调用 Google API。
创建服务帐号
服务帐号的凭据包含生成的电子邮件地址,该地址是唯一的,且至少有一个公钥/私钥对。如果启用了全网域授权,则客户端 ID 也是服务帐号凭据的一部分。
如果您的应用在 Google App Engine 上运行,则在您创建项目时会自动设置服务帐号。
如果您的应用在 Google Compute Engine 上运行,则您在创建项目时也会自动设置服务帐号,但您必须在创建 Google Compute Engine 实例时指定应用需要访问的范围。如需了解详情,请参阅准备实例以使用服务帐号。
如果您的应用未在 Google App Engine 或 Google Compute Engine 上运行,您必须在 中获取这些凭据。如需生成服务帐号凭据,或查看您已经生成的公共凭据,请执行以下操作:
首先,创建一个服务帐户:
- 打开 Service accounts page。
- If prompted, select a project, or create a new one.
- 单击 创建服务帐户。
- 在Service account details下,键入服务帐户的名称、ID 和描述,然后点击Create and continue 。
- 可选:在Grant this service account access to project下,选择要授予服务帐户的 IAM 角色。
- 单击继续。
- 可选:在Grant users access to this service account下,添加允许使用和管理服务帐户的用户或组。
- 单击完成。
接下来,创建一个服务帐户密钥:
- 单击您创建的服务帐户的电子邮件地址。
- 单击密钥选项卡。
- 在添加密钥下拉列表中,选择创建新密钥。
- 单击创建。
您的新公钥/私钥对已生成并下载到您的机器上;它作为私钥的唯一副本。您有责任安全地存储它。如果您丢失了这个密钥对,您将需要生成一个新的。
您可以随时返回 API Console 查看电子邮件地址、公钥指纹和其他信息,也可以生成其他公钥/私钥对。如需详细了解 API Console中的服务帐号凭据,请参阅 API Console帮助文件中的服务帐号。
记下服务帐号的电子邮件地址,并将服务帐号的私钥文件存储在您的应用可以访问的位置。您的应用需要它们来进行已获授权的 API 调用。
将全网域授权委派给服务帐号
如果您有 Google Workspace 帐号,则组织管理员可以授权应用代表 Google Workspace 网域中的用户访问用户数据。例如,如果一个应用使用 Google Calendar API 向 Google Workspace 网域中所有用户的日历添加活动,则该应用会使用服务帐号代表用户访问 Google Calendar API。授权服务帐号代表网域中的用户访问数据有时称为“将全网域授权委派给服务帐号”。
如需将全网域授权委派给服务帐号,Google Workspace 网域的超级用户必须完成以下步骤:
- 在 Google Workspace 网域的 管理控制台中,转到主菜单 > 安全性 > 访问权限和数据控件 > API 控件。
- 在全网域授权窗格中,选择管理全网域授权。
- 点击新增。
- 在客户端 ID 字段中,输入服务帐号的客户端 ID。您可以在 Service accounts page中找到服务帐号的客户端 ID。
- 在 OAuth 范围(以英文逗号分隔)字段中,输入应用应被访问的范围列表。例如,如果您的应用需要对 Google Drive API 和 Google Calendar API 拥有全网域访问权限,请输入:https://www.googleapis.com/auth/drive, https://www.googleapis.com/auth/calendar。
- 点击授权。
您的应用现在有权以您网域内的用户的身份进行 API 调用(以“模拟”用户)。当您准备进行已获授权的 API 调用时,请指定要模拟的用户。
准备进行已获授权的 API 调用
Java
从 API Console获取客户端电子邮件地址和私钥后,请使用 Java 版 Google API 客户端库根据服务帐号的凭据和应用需要访问的范围创建一个 GoogleCredential
对象。例如:
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; import com.google.api.services.sqladmin.SQLAdminScopes; // ... GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json")) .createScoped(Collections.singleton(SQLAdminScopes.SQLSERVICE_ADMIN));
如果要在 Google Cloud Platform 上开发应用,您可以改用应用默认凭据,以简化流程。
将全网域授权
如果您对该服务帐号进行了委派全网域访问权限,并且想要模拟用户帐号,请使用 GoogleCredential
对象的 createDelegated
方法指定该用户帐号的电子邮件地址。例如:
GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json")) .createScoped(Collections.singleton(SQLAdminScopes.SQLSERVICE_ADMIN)) .createDelegated("user@example.com");
使用 GoogleCredential
对象在应用中调用 Google API。
Python
从 API Console获取客户端电子邮件地址和私钥后,请使用 Python 版 Google API 客户端库完成以下步骤:
- 根据服务帐号的凭据和应用需要访问的范围创建一个
Credentials
对象。例如:from google.oauth2 import service_account SCOPES = ['https://www.googleapis.com/auth/sqlservice.admin'] SERVICE_ACCOUNT_FILE = '/path/to/service.json' credentials = service_account.Credentials.from_service_account_file( SERVICE_ACCOUNT_FILE, scopes=SCOPES)
如果要在 Google Cloud Platform 上开发应用,您可以改用应用默认凭据,以简化流程。
- 委托全网域授权
如果您对该服务帐号进行了委派全网域访问权限,并且想要模拟用户帐号,请使用现有
ServiceAccountCredentials
对象的with_subject
方法。例如:delegated_credentials = credentials.with_subject('user@example.org')
使用 Credentials 对象在应用中调用 Google API。
HTTP/REST
从 API Console获取客户端 ID 和私钥后,您的应用需要完成以下步骤:
- 创建一个 JSON Web 令牌(JWT,这读作“jot”),其中包含标头、声明集和签名。
- 从 Google OAuth 2.0 授权服务器请求访问令牌。
- 处理授权服务器返回的 JSON 响应。
下面几部分介绍了如何完成这些步骤。
如果响应中包含访问令牌,您可以使用该访问令牌调用 Google API。(如果响应不包含访问令牌,则表示您的 JWT 和令牌请求可能格式不正确,或者该服务帐号可能无权访问所请求的范围。)
当访问令牌到期时,您的应用会另外生成一个 JWT,为其签名,并请求另一个访问令牌。

本部分的其余内容详细介绍了如何创建 JWT、为 JWT 签名、形成访问令牌请求和处理响应。
创建 JWT
JWT 由三个部分组成:标头、声明集和签名。标头和声明集是 JSON 对象。这些 JSON 对象已序列化为 UTF-8 字节,然后使用 Base64url 编码进行编码。这种编码可以防止由于重复编码操作而导致编码更改的弹性。标头、声明集和签名通过英文句点 (.
) 字符串联起来。
JWT 的组成如下所示:
{Base64url encoded header}.{Base64url encoded claim set}.{Base64url encoded signature}
签名的基本字符串如下所示:
{Base64url encoded header}.{Base64url encoded claim set}
构建 JWT 标头
该标头包含两个字段,用于指示签名算法和断言的格式。这两个字段均为必填字段,且每个字段只有一个值。随着其他算法和格式的引入,此头文件也会相应变化。
服务帐号依赖于 RSA SHA-256 算法和 JWT 令牌格式。因此,标头的 JSON 表示法如下所示:
{"alg":"RS256","typ":"JWT"}
此参数的 Base64url 表示形式如下所示:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9
形成 JWT 声明集
JWT 声明集包含有关 JWT 的信息,包括请求的权限(范围)、令牌的目标、颁发者、令牌的发出时间以及令牌的生命周期。大部分字段均为必填字段。与 JWT 标头一样,JWT 声明集也是 JSON 对象,用于计算签名。
必需的声明
JWT 声明集中必需的声明如下所示。它们可能以声明集中的任意顺序出现。
名称 | 说明 |
---|---|
iss |
服务帐号的电子邮件地址。 |
scope |
应用请求的权限的列表(以空格分隔)。 |
aud |
断言的预期目标的描述符。发出访问令牌请求时,此值始终为 https://oauth2.googleapis.com/token 。 |
exp |
断言的到期时间,以世界协调时间 (UTC) 1970 年 1 月 1 日 00:00:00 以来的秒数指定。此值在发出时间后最多 1 小时。 |
iat |
断言的发出时间,以自世界协调时间 (UTC) 1970 年 1 月 1 日 00:00:00 以来的秒数指定。 |
JWT 声明集中必填字段的 JSON 表示法如下所示:
{ "iss": "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com", "scope": "https://www.googleapis.com/auth/devstorage.read_only", "aud": "https://oauth2.googleapis.com/token", "exp": 1328554385, "iat": 1328550785 }
其他版权主张
在某些企业情况下,应用可以使用全网域授权来代表组织中的特定用户执行操作。必须先授予执行此类模拟的权限,然后应用才能模拟用户,并且通常由超级用户处理。如需了解详情,请参阅通过全网域授权来控制 API 访问权限。
如需获取可授予应用对资源的访问权限的访问令牌,请在 JWT 声明中包含用户的电子邮件地址,并将其设为 sub
字段的值。
名称 | 说明 |
---|---|
sub |
应用请求委托的访问权限的用户的电子邮件地址。 |
如果某个应用无权模拟用户,则对包含 sub
字段的访问令牌请求的响应将是错误。
下面显示了包含 sub
字段的 JWT 声明集示例:
{ "iss": "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com", "sub": "some.user@example.com", "scope": "https://www.googleapis.com/auth/prediction", "aud": "https://oauth2.googleapis.com/token", "exp": 1328554385, "iat": 1328550785 }
编码 JWT 声明集
与 JWT 标头一样,JWT 声明集应序列化为采用 UTF-8 和 Base64url-safe 编码。以下是 JWT 声明集的 JSON 表示法示例:
{ "iss": "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com", "scope": "https://www.googleapis.com/auth/prediction", "aud": "https://oauth2.googleapis.com/token", "exp": 1328554385, "iat": 1328550785 }
计算签名
JSON Web 签名 (JWS) 是规范 JWT 签名机制的规范。该签名的输入是以下内容的字节数组:
{Base64url encoded header}.{Base64url encoded claim set}
计算签名时,必须使用 JWT 标头中的签名算法。Google OAuth 2.0 授权服务器唯一支持的签名算法是采用 SHA-256 哈希算法的 RSA。这在 JWT 标头的 alg
字段中表示为 RS256
。
使用从 Google API Console获取的私钥,使用 SHA256withRSA(也称为 RSASSA-PKCS1-V1_5-SIGN,使用 SHA-256 哈希函数)为输入的 UTF-8 表示法签名。输出将是一个字节数组。
然后,签名必须进行 Base64url 编码。标头、声明集和签名通过英文句点 (.
) 字符串联起来。结果就是 JWT。您应如下所示(为清楚起见,添加了换行符):
{Base64url encoded header}. {Base64url encoded claim set}. {Base64url encoded signature}
以下是采用 Base64url 编码的 JWT 示例:
{"alg":"RS256","typ":"JWT"}. { "iss":"761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com", "scope":"https://www.googleapis.com/auth/prediction", "aud":"https://oauth2.googleapis.com/token", "exp":1328554385, "iat":1328550785 }. [signature bytes]
以下是已签署并且可以传输的 JWT 示例:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL29hdXRoMi92NC90b2tlbiIsImV4cCI6MTMyODU1NDM4NSwiaWF0IjoxMzI4NTUwNzg1fQ.UFUt59SUM2_AW4cRU8Y0BYVQsNTo4n7AFsNrqOpYiICDu37vVt-tw38UKzjmUKtcRsLLjrR3gFW3dNDMx_pL9DVjgVHDdYirtrCekUHOYoa1CMR66nxep5q5cBQ4y4u2kIgSvChCTc9pmLLNoIem-ruCecAJYgI9Ks7pTnW1gkOKs0x3YpiLpzplVHAkkHztaXiJdtpBcY1OXyo6jTQCa3Lk2Q3va1dPkh_d--GU2M5flgd8xNBPYw4vxyt0mP59XZlHMpztZt0soSgObf7G3GXArreF_6tpbFsS3z2t5zkEiHuWJXpzcYr5zWTRPDEHsejeBSG8EgpLDce2380ROQ
发出访问令牌请求
生成已签名的 JWT 后,应用可以使用它来请求访问令牌。此访问令牌请求是 HTTPS POST
请求,并且正文经过了网址编码。网址如下所示:
https://oauth2.googleapis.com/token
HTTPS POST
请求需要以下参数:
名称 | 说明 |
---|---|
grant_type |
使用以下字符串,根据需要进行网址编码:
urn:ietf:params:oauth:grant-type:jwt-bearer |
assertion |
JWT,包括签名。 |
以下是访问令牌请求中使用的 HTTPS POST
请求的原始转储:
POST /token HTTP/1.1 Host: oauth2.googleapis.com Content-Type: application/x-www-form-urlencoded grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsImV4cCI6MTMyODU3MzM4MSwiaWF0IjoxMzI4NTY5NzgxfQ.ixOUGehweEVX_UKXv5BbbwVEdcz6AYS-6uQV6fGorGKrHf3LIJnyREw9evE-gs2bmMaQI5_UbabvI4k-mQE4kBqtmSpTzxYBL1TCd7Kv5nTZoUC1CmwmWCFqT9RE6D7XSgPUh_jF1qskLa2w0rxMSjwruNKbysgRNctZPln7cqQ
以下是同样使用 curl
的请求:
curl -d 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsImV4cCI6MTMyODU3MzM4MSwiaWF0IjoxMzI4NTY5NzgxfQ.RZVpzWygMLuL-n3GwjW1_yhQhrqDacyvaXkuf8HcJl8EtXYjGjMaW5oiM5cgAaIorrqgYlp4DPF_GuncFqg9uDZrx7pMmCZ_yHfxhSCXru3gbXrZvAIicNQZMFxrEEn4REVuq7DjkTMyCMGCY1dpMa8aWfTQFt3Eh7smLchaZsU ' https://oauth2.googleapis.com/token
处理响应
如果 JWT 和访问令牌请求的格式正确,并且服务帐号有权执行该操作,则来自授权服务器的 JSON 响应将包含一个访问令牌。以下是响应示例:
{ "access_token": "1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M", "scope": "https://www.googleapis.com/auth/prediction" "token_type": "Bearer", "expires_in": 3600 }
访问令牌可以在 expires_in
值指定的时长内重复使用。
调用 Google API
Java
完成以下步骤,使用 GoogleCredential
对象调用 Google API:
- 使用
GoogleCredential
对象为您要调用的 API 创建一个服务对象。例如:SQLAdmin sqladmin = new SQLAdmin.Builder(httpTransport, JSON_FACTORY, credential).build();
- 使用服务对象提供的接口向 API 服务发出请求。例如,如需列出令人兴奋的 example-123 项目中的 Cloud SQL 数据库实例,请运行以下命令:
SQLAdmin.Instances.List instances = sqladmin.instances().list("exciting-example-123").execute();
Python
完成以下步骤后,使用已获授权的 Credentials
对象调用 Google API:
- 为您要调用的 API 构建服务对象。您可以通过使用 API 的名称和版本以及已获授权的
Credentials
对象调用build
函数来构建服务对象。例如,如需调用 Cloud SQL Administration API 的版本 1beta3,请运行以下命令:import googleapiclient.discovery sqladmin = googleapiclient.discovery.build('sqladmin', 'v1beta3', credentials=credentials)
- 使用服务对象提供的接口向 API 服务发出请求。例如,如需列出令人兴奋的 example-123 项目中的 Cloud SQL 数据库实例,请运行以下命令:
response = sqladmin.instances().list(project='exciting-example-123').execute()
HTTP/REST
在您的应用获得访问令牌后,您可以使用该令牌代表指定的服务帐号或用户帐号调用 Google API(如果已授予该 API 所需的访问权限范围)。为此,请在向 API 发出的请求中添加访问令牌,具体方法是添加 access_token
查询参数或 Authorization
HTTP 标头 Bearer
值。请尽可能使用 HTTP 标头,因为查询字符串通常会显示在服务器日志中。在大多数情况下,您可以使用客户端库来设置对 Google API 的调用(例如,在调用 Drive Files API 时)。
您可以访问 OAuth 2.0 Playground,试用所有 Google API 并查看其范围。
HTTP GET 示例
使用 Authorization: Bearer
HTTP 标头调用
drive.files
端点 (Drive Files API) 的过程可能如下所示。请注意,您需要指定自己的访问令牌:
GET /drive/v2/files HTTP/1.1 Host: www.googleapis.com Authorization: Bearer access_token
以下是使用 access_token
查询字符串参数对经过身份验证的用户的同一 API 的调用:
GET https://www.googleapis.com/drive/v2/files?access_token=access_token
curl
示例
您可以使用 curl
命令行应用测试这些命令。下面是一个使用 HTTP 标头选项(首选)的示例:
curl -H "Authorization: Bearer access_token" https://www.googleapis.com/drive/v2/files
或者,查询字符串参数选项:
curl https://www.googleapis.com/drive/v2/files?access_token=access_token
访问令牌何时过期
Google OAuth 2.0 授权服务器发出的访问令牌会在 expires_in
值提供的期限过后过期。当访问令牌到期时,应用应生成另一个 JWT,为其签名,并请求另一个访问令牌。
JWT 错误代码
error 字段 |
error_description 字段 |
含义 | 解决方法 |
---|---|---|---|
unauthorized_client |
Unauthorized client or scope in request. |
如果您尝试使用全网域授权,则服务帐号未在用户网域的管理控制台中授权。 |
确保在管理控制台的
全网域授权页面针对 虽然授权过程通常需要几分钟的时间,但授权最长可能需要 24 小时才能应用到 Google 帐号中的所有用户。 |
unauthorized_client |
Client is unauthorized to retrieve access tokens using this method, or client not
authorized for any of the scopes requested. |
服务帐号在管理控制台中使用客户端电子邮件地址(而非数字 ID)进行授权。 | 在管理控制台的 全网域授权页面中,移除该客户端,然后使用数字 ID 重新添加该客户端。 |
access_denied |
(任何值) | 如果您使用的是全网域授权,则管理控制台中的一个或多个请求的范围未获得授权。 |
请确保管理控制台在
全网域授权页面中针对相应 虽然授权过程通常需要几分钟的时间,但授权最长可能需要 24 小时才能应用到 Google 帐号中的所有用户。 |
admin_policy_enforced |
(任何值) | 根据其 Google Workspace 管理员的政策,对应的 Google 帐号无法向所请求的一个或多个范围授权。 |
请参阅 Google Workspace 管理员帮助文章控制哪些第三方应用和内部应用可以访问 Google Workspace 数据。详细了解管理员如何限制对所有范围或敏感和受限范围的访问权限,直到明确向您的 OAuth 客户端 ID 授予访问权限为止。 |
invalid_client |
(任何值) |
OAuth 客户端或 JWT 令牌无效或配置有误。 如需了解详情,请参阅错误说明。 |
请确保 JWT 令牌有效且包含正确的声明。 请检查 OAuth 客户端和服务帐号是否已正确配置,以及您使用的电子邮件地址是否正确。 检查 JWT 令牌是否正确,是否已针对请求中的客户端 ID 发出。 |
invalid_grant |
Not a valid email. |
该用户不存在。 | 请检查 sub 声明(字段)中的电子邮件地址是否正确。 |
invalid_grant |
|
这通常意味着本地系统时间不正确。如果 exp 值与 iat 值相差超过 65 分钟,或者 exp 值低于 iat 值,也有可能发生这种情况。 |
确保生成 JWT 的系统上的时钟正确无误。如有必要,请将您的时间与 Google NTP 同步。 |
invalid_grant |
Invalid JWT Signature. |
JWT 断言使用的私钥未与客户端电子邮件标识的服务帐号关联,或者所用的密钥已被删除、停用或已过期。 或者,JWT 断言可能编码不正确 - 它必须采用 Base64 编码,没有换行符或填充等号。 |
解码 JWT 声明集,并验证对断言进行签名的密钥与服务帐号相关联。 尝试使用 Google 提供的 OAuth 库,以确保正确生成 JWT。 |
invalid_scope |
Invalid OAuth scope or ID token audience provided. |
未请求任何范围(范围列表为空),或请求的某个范围不存在(即无效)。 |
确保 JWT 的 请注意, |
disabled_client |
The OAuth client was disabled. |
用于签署 JWT 断言的密钥已停用。 |
转到 Google API Console,然后在 IAM 和管理 > 服务帐号下启用包含用于对断言签名的“密钥 ID”的服务帐号。 |
org_internal |
This client is restricted to users within its organization. |
请求中的 OAuth 客户端 ID 属于某个项目,该项目限制对特定 Google Cloud 组织中的 Google 帐号的访问权限。 |
使用组织的服务帐号进行身份验证。确认您的 OAuth 应用的用户类型配置。 |
附录:无 OAuth 的服务帐号授权
对于某些 Google API,您可以直接将已签名的 JWT 用作不记名令牌(而不是 OAuth 2.0 访问令牌)来执行已获授权的 API 调用。如有可能,您可以避免在进行 API 调用之前向 Google 的授权服务器发出网络请求。
如果要调用的 API 在 Google API GitHub 代码库中发布了服务定义,您可以使用 JWT 而不是访问令牌进行已获授权的 API 调用。为此,请执行以下操作:
- 按照上述说明创建服务帐号。请务必保留创建帐号时获得的 JSON 文件。
- 使用任何标准 JWT 库(例如可在 jwt.io 中找到的库)创建带有标头和载荷的 JWT,如以下示例所示:
{ "alg": "RS256", "typ": "JWT", "kid": "abcdef1234567890" } . { "iss": "123456-compute@developer.gserviceaccount.com", "sub": "123456-compute@developer.gserviceaccount.com", "aud": "https://firestore.googleapis.com/", "iat": 1511900000, "exp": 1511903600 }
- 对于标头中的
kid
字段,请指定您的服务帐号的私钥 ID。您可以在服务帐号 JSON 文件的private_key_id
字段中找到此值。 - 对于
iss
和sub
字段,请指定您的服务帐号的电子邮件地址。您可以在服务帐号 JSON 文件的client_email
字段中找到此值。 - 对于
aud
字段,请指定 API 端点。例如:https://SERVICE.googleapis.com/
。 - 对于
iat
字段,请指定当前的 Unix 时间;对于exp
字段,请指定正好在 3600 秒后 JWT 到期的时间。
使用服务帐号 JSON 文件中的私钥,使用 RSA-256 为 JWT 签名。
例如:
Java
使用 google-api-java-client 和 java-jwt:
GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json")); PrivateKey privateKey = credential.getServiceAccountPrivateKey(); String privateKeyId = credential.getServiceAccountPrivateKeyId(); long now = System.currentTimeMillis(); try { Algorithm algorithm = Algorithm.RSA256(null, privateKey); String signedJwt = JWT.create() .withKeyId(privateKeyId) .withIssuer("123456-compute@developer.gserviceaccount.com") .withSubject("123456-compute@developer.gserviceaccount.com") .withAudience("https://firestore.googleapis.com/") .withIssuedAt(new Date(now)) .withExpiresAt(new Date(now + 3600 * 1000L)) .sign(algorithm); } catch ...
Python
使用 PyJWT:
iat = time.time() exp = iat + 3600 payload = {'iss': '123456-compute@developer.gserviceaccount.com', 'sub': '123456-compute@developer.gserviceaccount.com', 'aud': 'https://firestore.googleapis.com/', 'iat': iat, 'exp': exp} additional_headers = {'kid': PRIVATE_KEY_ID_FROM_JSON} signed_jwt = jwt.encode(payload, PRIVATE_KEY_FROM_JSON, headers=additional_headers, algorithm='RS256')
- 使用已签名的 JWT 作为不记名令牌调用 API:
GET /v1/projects/abc/databases/123/indexes HTTP/1.1 Authorization: Bearer SIGNED_JWT Host: firestore.googleapis.com