使用OAuth 2.0进行服务器到服务器的应用程序

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流。

概述

要支持服务器到服务器的交互,请首先在API Console中为您的项目创建一个服务帐户。如果要访问Google Workspace帐户中用户的用户数据,则将对服务帐户的全域访问权限委派给该用户。

然后,您的应用程序准备通过使用服务帐户的凭据从OAuth 2.0身份验证服务器请求访问令牌来进行授权的API调用。

最后,您的应用程序可以使用访问令牌来调用Google API。

创建服务帐户

服务帐户的凭据包括生成的唯一电子邮件地址和至少一个公共/专用密钥对。如果启用了域范围的委派,则客户端ID也是服务帐户凭据的一部分。

如果您的应用程序在Google App Engine上运行,则在您创建项目时会自动设置一个服务帐户。

如果您的应用程序在Google Compute Engine上运行,则在创建项目时也会自动设置一个服务帐户,但是在创建Google Compute Engine实例时,您必须指定应用程序需要访问的范围。有关更多信息,请参阅准备实例以使用服务帐户

如果您的应用程序不在Google App Engine或Google Compute Engine上运行,则必须在Google API Console中获取这些凭据。要生成服务帐户凭据,或查看已经生成的公共凭据,请执行以下操作:

  1. 打开Service accounts page
  2. If prompted, select a project, or create a new one.
  3. 单击 创建服务帐户
  4. 在“ 服务帐户详细信息”下 ,键入服务帐户的名称,ID和描述,然后单击“ 创建”
  5. 可选:在“ 服务帐户权限”下 ,选择要授予服务帐户的IAM角色,然后单击继续
  6. 可选:在“ 授予用户对此服务帐户的访问权限”下 ,添加允许使用和管理该服务帐户的用户或组。
  7. 单击 创建密钥 ,然后单击创建

您的新公钥/私钥对已生成并下载到您的计算机;它充当私钥的唯一副本。您有责任安全地存储它。如果丢失此密钥对,则需要生成一个新的密钥对。

如果您需要向服务帐户授予G Suite域范围内的权限 ,请单击您创建的服务帐户的电子邮件地址,然后从“ 唯一ID”框中复制该值。

要将权限委派给服务帐户,请使用复制的值作为客户端ID。

您可以随时返回API Console ,以查看电子邮件地址,公钥指纹和其他信息,或生成其他公/私钥对。有关API Console中服务帐户凭据的更多详细信息,请参阅API Console帮助文件中的 服务帐户

记下服务帐户的电子邮件地址,并将服务帐户的私钥文件存储在应用程序可访问的位置。您的应用程序需要它们进行授权的API调用。

将域范围的权限委派给服务帐户

如果您拥有Google Workspace帐户,则组织的管理员可以授权应用程序代表Google Workspace域中的用户访问用户数据。例如,使用Google Calendar API将事件添加到Google Workspace域中所有用户的日历的应用程序将使用服务帐户代表用户访问Google Calendar API。授权服务帐户代表域中的用户访问数据有时称为“将域范围的权限委派给服务帐户”。

要将域范围的权限委派给服务帐户,请首先在Service accounts page中为现有服务帐户启用域范围的委派,或者创建一个启用了域范围的委派的新服务帐户

然后,Google Workspace域的超级管理员必须完成以下步骤:

  1. 在您的Google Workspace域的管理控制台中,转到主菜单 >安全> API控件
  2. 在“域范围委派”窗格中,选择“管理域范围委派”
  3. 单击添加新的
  4. 客户ID字段中,输入服务帐户的客户ID 。您可以在Service accounts page中找到服务帐户的客户ID。
  5. OAuth范围(以逗号分隔)字段中,输入应授予您的应用程序访问权限的范围列表。例如,如果您的应用程序需要在域范围内对Google Drive API和Google Calendar API的完全访问权限,请输入: https : //www.googleapis.com/auth/drive,https://www.googleapis.com/auth / calendar
  6. 点击授权

您的应用程序现在有权以您域中的用户身份进行API调用(“模拟”用户)。准备进行授权的API调用时,请指定要模拟的用户。

准备进行授权的API调用

爪哇

从API Console获取客户端电子邮件地址和私钥后,请使用JavaGoogle 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获取客户端电子邮件地址和私钥后,请使用适用于PythonGoogle API客户端库完成以下步骤:

  1. 根据服务帐户的凭据以及您的应用程序需要访问的范围创建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上开发应用程序,则可以改用应用程序默认凭据,这样可以简化流程。

  2. 委托域范围的权限

    如果您已委派了对服务帐户的域范围访问权限,并且要模拟用户帐户,请使用现有ServiceAccountCredentials对象的with_subject方法。例如:

    delegated_credentials = credentials.with_subject('user@example.org')

使用凭据对象在您的应用程序中调用Google API。

HTTP / REST

从API Console获得客户端ID和私钥后,您的应用程序需要完成以下步骤:

  1. 创建一个JSON Web令牌(JWT,发音为“ jot”),其中包括标头,声明集和签名。
  2. 向Google OAuth 2.0授权服务器请求访问令牌。
  3. 处理授权服务器返回的JSON响应。

以下各节描述了如何完成这些步骤。

如果响应中包含访问令牌,则可以使用该访问令牌来调用Google API 。 (如果响应中不包含访问令牌,则您的JWT和令牌请求可能格式不正确,或者服务帐户可能无权访问所请求的范围。)

当访问令牌到期时,您的应用程序将生成另一个JWT,对其进行签名,然后请求另一个访问令牌。

您的服务器应用程序使用JWT从Google授权服务器请求令牌,然后使用令牌调用Google API端点。没有最终用户参与。

本节的其余部分描述创建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声明的到期时间,指定为自1970年1月1日00:00:00 UTC以来的秒数。此值在发出时间之后最长为1小时。
iat发出声明的时间,指定为自1970年1月1日UTC 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字段的访问令牌请求的响应将为error

包含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安全编码。以下是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(也称为带有SHA-256哈希函数的RSASSA-PKCS1-V1_5-SIGN)对输入的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请求,主体是URL编码的。 URL显示如下:

https://oauth2.googleapis.com/token

HTTPS POST请求中需要以下参数:

名称描述
grant_type使用以下字符串,并根据需要进行URL编码: 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

爪哇

完成以下步骤,使用GoogleCredential对象调用Google API:

  1. 为您要使用GoogleCredential对象调用的API创建服务对象。例如:
    SQLAdmin sqladmin =
        new SQLAdmin.Builder(httpTransport, JSON_FACTORY, credential).build();
  2. 使用服务对象提供接口向API服务发出请求。例如,要列出令人兴奋的示例123项目中的Cloud SQL数据库实例:
    SQLAdmin.Instances.List instances =
        sqladmin.instances().list("exciting-example-123").execute();

Python

完成以下步骤,使用授权的Credentials对象调用Google API:

  1. 为您要调用的API构建服务对象。您可以通过使用API​​的名称和版本以及授权的Credentials对象调用build函数来构建服务对象。例如,要调用Cloud SQL管理API的版本1beta3:
    import googleapiclient.discovery
    
    sqladmin = googleapiclient.discovery.build('sqladmin', 'v1beta3', credentials=credentials)
  2. 使用服务对象提供接口向API服务发出请求。例如,要列出令人兴奋的示例123项目中的Cloud SQL数据库实例:
    response = sqladmin.instances().list(project='exciting-example-123').execute()

HTTP / REST

您的应用程序获取访问令牌后,如果已授予API所需的访问范围,则可以使用该令牌代表给定的服务帐户或用户帐户对Google API进行调用。为此,请通过包含access_token查询参数或Authorization HTTP标头Bearer值,在对API的请求中包含访问令牌。如果可能,最好使用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.如果您尝试使用域范围内的委派,则该服务帐户未在用户域的管理控制台中被授权。

确保在管理控制台的“域范围委派”页面中为sub声明(字段)中的用户授权了该服务帐户。

虽然通常需要几分钟,但授权最多可能需要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 (任何值)如果您使用的是域范围内的委派,则管理控制台不会授权一个或多个请求的作用域。

确保服务帐户被授权在域范围内的代表团为在用户管理控制台的页sub要求(场),而且它包括所有的作用域你请求的scope的JWT的要求。

虽然通常需要几分钟,但授权最多可能需要24小时才能传播到您Google帐户中的所有用户。

invalid_grant Not a valid email.该用户不存在。检查sub声明(字段)中的电子邮件地址是否正确。
invalid_grant

Invalid JWT: Token must be a short-lived token (60 minutes) and in a reasonable timeframe. Check your 'iat' and 'exp' values and use a clock with skew to account for clock differences between systems.

通常,这意味着本地系统时间不正确。如果它也可能发生exp值是从在未来超过65分钟iat值,或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的scope声明(字段),并将其包含的范围与要使用的API的已记录范围进行比较,以确保没有错误或拼写错误。

请注意,范围声明中的scope列表需要用空格而不是逗号分隔。

disabled_client The OAuth client was disabled.用于签名JWT断言的密钥已禁用。

转到Google API Console,然后在IAM&Admin> Service Accounts下,启用包含用于签署断言的“密钥ID”的服务帐户。

附录:无OAuth的服务帐户授权

使用某些Google API,您可以直接使用签名的JWT作为承载令牌(而不是OAuth 2.0访问令牌)进行授权的API调用。在可能的情况下,您可以避免在进行API调用之前不必向Google的授权服务器发出网络请求。

如果要调用的API在Google API GitHub存储库中发布了服务定义,则可以使用JWT而不是访问令牌进行授权的API调用。为此:

  1. 如上所述,创建一个服务帐户。创建帐户时,请确保保留您获取的JSON文件。
  2. 使用任何标准的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字段中找到此值。
    • isssub字段中,指定服务帐户的电子邮件地址。您可以在服务帐户JSON文件的client_email字段中找到此值。
    • aud字段中,指定API端点。例如: https:// SERVICE .googleapis.com/
    • 对于iat字段,请指定当前的Unix时间,对于exp字段,请指定JWT到期的确切3600秒后的时间。

使用在服务帐户JSON文件中找到的私钥,使用RSA-256对JWT进行签名。

例如:

爪哇

使用google-api-java-clientjava-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')
  1. 使用签名的JWT作为承载令牌来调用API:
    GET /v1/projects/abc/databases/123/indexes HTTP/1.1
    Authorization: Bearer SIGNED_JWT
    Host: firestore.googleapis.com