使用服务账号以 Apps 脚本项目身份进行身份验证

本指南介绍了在 Apps 脚本中调用 API 时如何使用服务账号进行身份验证。

服务账号是一种由应用(而非真人)使用的特殊账号。您可以使用服务账号通过机器人账号访问数据或执行操作,也可以代表 Google Workspace 或 Cloud Identity 用户访问数据。如需了解详情,请参阅了解服务账号

如需大致了解 Google Workspace API 的身份验证,请参阅创建访问凭据

何时在 Apps 脚本中使用服务账号

以下是一些原因,您可能会考虑使用服务账号身份验证,而不是其他身份验证方法(例如 ScriptApp.getOAuthToken()):

  • 通过 Google Cloud API 和服务获得更出色的性能:许多 Google Cloud API 都是为服务账号身份验证而设计的。服务账号还可以提供一种更集成、可靠且安全的方式来与大多数 API 进行交互。
  • 解耦的权限:服务账号拥有自己的权限,与任何用户分开。当您与其他用户共享 Apps 脚本项目时,身份验证方法 ScriptApp.getOAuthToken() 可能会失败。通过使用服务账号,您可以共享脚本并将其发布为 Google Workspace 插件
  • 自动化脚本和长时间运行的任务:借助服务账号,您可以运行自动化脚本、批处理进程或后台任务,而无需用户输入。
  • 增强的安全性和最小权限原则:您可以向服务账号授予特定权限,使其只能访问所需的资源。这遵循了最小权限原则,可降低安全风险。使用 ScriptApp.getOAuthToken() 通常会授予脚本所有用户权限,这可能过于宽泛。
  • 集中式访问权限管理:服务账号使用 Google Cloud 的 Identity and Access Management (IAM) 进行管理。IAM 可帮助 Google Workspace 组织管理对 Apps 脚本项目内经过身份验证的服务的访问权限。

前提条件

创建服务账号

在 Cloud 项目中,创建服务账号:

Google Cloud 控制台

  1. 在 Google Cloud 控制台中,依次前往“菜单”图标 > IAM 和管理 > 服务账号

    转到“服务账号”

  2. 点击创建服务账号
  3. 填写服务账号详细信息,然后点击创建并继续
  4. 可选:向您的服务账号分配角色,以授予对 Google Cloud 项目资源的访问权限。如需了解详情,请参阅授予、更改和撤消对资源的访问权限
  5. 点击继续
  6. 可选:输入可管理此服务账号并使用此服务账号执行操作的用户或群组。如需了解详情,请参阅管理服务账号模拟
  7. 点击完成。记下服务账号的电子邮件地址。

gcloud CLI

  1. 创建服务账号:
    gcloud iam service-accounts create SERVICE_ACCOUNT_NAME \
      --display-name="SERVICE_ACCOUNT_NAME"
  2. 可选:向您的服务账号分配角色,以授予对 Google Cloud 项目资源的访问权限。如需了解详情,请参阅授予、更改和撤消对资源的访问权限

为服务账号分配角色

您必须通过超级管理员账号为服务账号分配预先创建的角色或自定义角色。

  1. 在 Google 管理控制台中,依次点击“菜单”图标 > 账号 > 管理员角色

    前往“管理员角色”

  2. 将光标指向要分配的角色,然后点击分配管理员角色

  3. 点击分配服务账号

  4. 输入服务账号的电子邮件地址。

  5. 依次点击添加 > 分配角色

为服务账号创建凭据

您需要获取公钥/私钥对形式的凭据。您的代码使用这些凭据来授权应用内的服务账号操作。

如需获取服务账号的凭据,请执行以下操作:

  1. 在 Google Cloud 控制台中,依次前往菜单 > IAM 和管理 > 服务账号

    转到“服务账号”

  2. 选择您的服务账号。
  3. 依次点击密钥 > 添加密钥 > 创建新密钥
  4. 选择 JSON,然后点击创建

    系统会生成新的公钥/私钥对,并以新文件的形式下载到您的计算机。将下载的 JSON 文件以 credentials.json 的名称保存到工作目录中。此文件是相应密钥的唯一副本。如需了解如何安全地存储密钥,请参阅管理服务账号密钥

  5. 点击关闭

复制 Cloud 项目编号

  1. 在 Google Cloud 控制台中,依次前往“菜单”图标 > IAM 和管理 > 设置

    前往“IAM 和管理”设置

  2. 项目编号字段中,复制相应值。

在 Apps 脚本项目中设置服务账号身份验证

本部分介绍了如何将 Cloud 项目中的服务账号凭据添加到 Apps 脚本项目。

在 Apps 脚本中设置 Cloud 项目

  1. 前往 Apps 脚本以打开或创建项目:


    打开 Apps 脚本

  2. 在您的 Apps 脚本项目中,点击项目设置 项目设置的图标

  3. Google Cloud Platform (GCP) 项目下,点击更改项目

  4. GCP 项目编号中,粘贴 Google Cloud 项目编号。

  5. 点击设置项目

将凭据保存为脚本属性

通过将服务账号凭据保存为 Apps 脚本项目设置中的脚本属性,安全地存储这些凭据:

  1. 复制您在上一部分中创建的服务账号 JSON 文件 (credentials.json) 的内容。
  2. 在您的 Apps 脚本项目中,前往项目设置
  3. 项目设置页面中,前往脚本属性,然后点击添加脚本属性并输入以下内容:
    • 媒体资源字段中,输入 SERVICE_ACCOUNT_KEY
    • Value 字段中,粘贴 JSON 密钥文件的内容。
  4. 点击保存脚本属性

添加 OAuth2 库

如需处理 OAuth2 身份验证流程,您可以使用 Apps 脚本库 apps-script-oauth2

如需将该库添加到您的 Apps 脚本项目,请执行以下操作:

  1. 在 Apps 脚本编辑器中,点击左侧旁边的添加库图标
  2. 脚本 ID 字段中,输入 1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF
  3. 点击查找
  4. 选择最新版本,然后点击添加

使用服务账号凭据调用 API

如需使用 Apps 脚本项目中的服务账号凭据,您可以使用以下函数 getServiceAccountService()

/**
 * Get a new OAuth2 service for a given service account.
 */
function getServiceAccountService() {
  const serviceAccountKeyString = PropertiesService.getScriptProperties()
      .getProperty('SERVICE_ACCOUNT_KEY');

  if (!serviceAccountKeyString) {
    throw new Error('SERVICE_ACCOUNT_KEY property is not set. ' +
        'Please follow the setup instructions.');
  }

  const serviceAccountKey = JSON.parse(serviceAccountKeyString);

  const CLIENT_EMAIL = serviceAccountKey.client_email;
  const PRIVATE_KEY = serviceAccountKey.private_key;

  // Replace with the specific scopes required for your API.
  const SCOPES = ['SCOPE'];

  return OAuth2.createService('ServiceAccount')
      .setTokenUrl('https://oauth2.googleapis.com/token')
      .setPrivateKey(PRIVATE_KEY)
      .setIssuer(CLIENT_EMAIL)
      .setPropertyStore(PropertiesService.getScriptProperties())
      .setScope(SCOPES);
}

SCOPE 替换为调用 API 所需的授权范围。该脚本使用您在上一步中保存为 SERVICE_ACCOUNT_KEY 脚本属性的服务账号凭据。

然后,您可以使用这些凭据来调用 API,如以下使用 UrlFetch 服务的示例所示:

function callApi() {
  const service = getServiceAccountService();

  // TODO(developer): Replace with the payload
  const payload = {};

  // TODO(developer): Replace with the API endpoint
  const response = UrlFetchApp.fetch('API_URL', {
    method: 'post',
    headers: {
      'Authorization': `Bearer ${service.getAccessToken()}`,
      'Content-Type': 'application/json',
    },
    payload: payload,
  });

  const result = JSON.parse(response.getContentText());

  return result;
}

API_URL 替换为您要调用的 HTTP 端点。