为发送请求提供授权

从您的应用服务器或受信任环境发送到 FCM 的请求必须经过授权。请注意旧版 HTTP 与 HTTP v1 API 授权之间存在以下重大差异:

  • FCM HTTP v1 API 使用短期有效的 OAuth 2.0 访问令牌为请求提供授权。 如需创建此令牌,您可以使用 Google 应用默认凭据(在 Google 服务器环境中)以及/或者手动从为服务帐号生成的 JSON 私钥文件中获取所需的凭据。 如果您使用 Firebase Admin SDK 发送消息,该库会为您处理令牌。
  • 旧版协议只能使用从 Firebase 控制台获取的长期有效的 API 密钥。

为 HTTP v1 发送请求 (send request) 提供授权

根据服务器环境的详细信息,您可以组合使用以下策略为服务器向 Firebase 服务发送的请求提供授权:

  • Google 应用默认凭据 (ADC)
  • 服务帐号 JSON 文件
  • 源自服务帐号的短期有效的 OAuth 2.0 访问令牌

如果您的应用在 Compute Engine、Google Kubernetes Engine、App Engine 或 Cloud Functions(包括 Cloud Functions for Firebase)上运行,请使用应用默认凭据 (ADC)。ADC 会使用您现有的默认服务帐号来获取用于为请求提供授权的凭据,并可通过环境变量 GOOGLE_APPLICATION_CREDENTIALS 实现灵活的本地测试。为了最大限度地自动化授权流程,请将 ADC 与 Admin SDK 服务器库搭配使用。

如果您的应用在非 Google 服务器环境中运行,则需要从 Firebase 项目下载服务帐号 JSON 文件。只要您有权访问包含私钥文件的文件系统,就可以通过环境变量 GOOGLE_APPLICATION_CREDENTIALS 利用这些手动获取的凭据为请求提供授权。如果您没有此类文件访问权限,则必须在代码中引用服务帐号文件,但这样做存在凭据泄露的风险,因此请务必谨慎。

使用 ADC 提供凭据

Google 应用默认凭据 (ADC) 按以下顺序查找您的凭据:

  1. ADC 检查是否已设置环境变量 GOOGLE_APPLICATION_CREDENTIALS。如果设置了该变量,ADC 就会使用该变量指向的服务帐号文件。

  2. 如果未设置该环境变量,则对于在 Compute Engine、Google Kubernetes Engine、App Engine 和 Cloud Functions 上运行的应用,ADC 会使用这些服务提供的默认服务帐号。

  3. 如果 ADC 无法使用上述任何凭据,系统会抛出一个错误。

以下 Admin SDK 代码示例展示了该策略。该示例并未明确指定应用凭据。但是,只要设置了该环境变量,或者只要应用在 Compute Engine、Google Kubernetes Engine、App Engine 或 Cloud Functions 上运行,ADC 便能够隐式查找凭据。

Node.js

admin.initializeApp({
  credential: admin.credential.applicationDefault(),
});

Java

FirebaseOptions options = FirebaseOptions.builder()
    .setCredentials(GoogleCredentials.getApplicationDefault())
    .setDatabaseUrl("https://<DATABASE_NAME>.firebaseio.com/")
    .build();

FirebaseApp.initializeApp(options);

Python

default_app = firebase_admin.initialize_app()

Go

app, err := firebase.NewApp(context.Background(), nil)
if err != nil {
	log.Fatalf("error initializing app: %v\n", err)
}

C#

FirebaseApp.Create(new AppOptions()
{
    Credential = GoogleCredential.GetApplicationDefault(),
});

手动提供凭据

Firebase 项目支持 Google 服务帐号,您可以使用这些帐号从应用服务器或受信任环境调用 Firebase 服务器 API。如果您是在本地编写代码,或是在本地部署您的应用,则可以使用通过此服务帐号获取的凭据来对服务器请求进行授权。

如需对服务帐号进行身份验证并授予其访问 Firebase 服务的权限,您必须生成 JSON 格式的私钥文件。

如需为您的服务帐号生成私钥文件,请执行以下操作:

  1. 在 Firebase 控制台中,打开设置 > 服务帐号

  2. 点击生成新的私钥,然后点击生成密钥进行确认。

  3. 妥善存储包含密钥的 JSON 文件。

通过服务帐号进行授权时,有两种方式可为您的应用提供凭据。您可以设置 GOOGLE_APPLICATION_CREDENTIALS 环境变量,也可以在代码中明确传递服务帐号密钥的路径。第一种方式更为安全,因此强烈推荐使用此方式。

如需设置该环境变量,请执行以下操作

将环境变量 GOOGLE_APPLICATION_CREDENTIALS 设置为包含服务帐号密钥的 JSON 文件的路径:此变量仅适用于当前的 Shell 会话,因此请在开始新的会话时重新设置该变量。

Linux 或 macOS

export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/service-account-file.json"

Windows

使用 PowerShell:

$env:GOOGLE_APPLICATION_CREDENTIALS="C:\Users\username\Downloads\service-account-file.json"

完成上述步骤后,应用默认凭据 (ADC) 便能隐式确定您的凭据,这样,在非 Google 环境中测试或运行应用时,您就能使用服务帐号凭据。

使用凭据来创建访问令牌

除非您使用自动处理授权的 Admin SDK,否则均需创建访问令牌并将其添加到发送请求中。

将您的 Firebase 凭据与适用于您的偏好语言的 Google Auth 库结合使用,以检索短期有效的 OAuth 2.0 访问令牌:

node.js

 function getAccessToken() {
  return new Promise(function(resolve, reject) {
    const key = require('../placeholders/service-account.json');
    const jwtClient = new google.auth.JWT(
      key.client_email,
      null,
      key.private_key,
      SCOPES,
      null
    );
    jwtClient.authorize(function(err, tokens) {
      if (err) {
        reject(err);
        return;
      }
      resolve(tokens.access_token);
    });
  });
}

在此示例中,Google API 客户端库使用 JSON Web 令牌 (JWT) 对请求进行身份验证。有关详情,请参阅 JSON Web 令牌

Python

def _get_access_token():
  """Retrieve a valid access token that can be used to authorize requests.

  :return: Access token.
  """
  credentials = service_account.Credentials.from_service_account_file(
    'service-account.json', scopes=SCOPES)
  request = google.auth.transport.requests.Request()
  credentials.refresh(request)
  return credentials.token

Java

private static String getAccessToken() throws IOException {
  GoogleCredentials googleCredentials = GoogleCredentials
          .fromStream(new FileInputStream("service-account.json"))
          .createScoped(Arrays.asList(SCOPES));
  googleCredentials.refreshAccessToken();
  return googleCredentials.getAccessToken().getTokenValue();
}

在您的访问令牌到期后,系统会自动调用令牌刷新方法以检索更新的访问令牌。

如需授予访问 FCM 的权限,则需请求 https://www.googleapis.com/auth/firebase.messaging 范围。

如需将访问令牌添加到 HTTP 请求标头中,请使用以下代码:

Authorization: Bearer <access_token> 格式将令牌添加为 Authorization 标头的值:

node.js

headers: {
  'Authorization': 'Bearer ' + accessToken
}

Python

headers = {
  'Authorization': 'Bearer ' + _get_access_token(),
  'Content-Type': 'application/json; UTF-8',
}

Java

URL url = new URL(BASE_URL + FCM_SEND_ENDPOINT);
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setRequestProperty("Authorization", "Bearer " + getAccessToken());
httpURLConnection.setRequestProperty("Content-Type", "application/json; UTF-8");
return httpURLConnection;

为旧版协议发送请求提供授权

使用 HTTP 旧版协议时,每个请求都必须包含来自 Firebase 控制台设置窗格的 Cloud Messaging 标签页的服务器密钥。对于 XMPP,您必须使用该服务器密钥来建立连接。

迁移旧服务器密钥

从 2020 年 3 月开始,FCM 已停止创建旧服务器密钥。 现有的旧服务器密钥仍然有效,但我们建议您改用 Firebase 控制台中标记为服务器密钥的新版密钥。

如果要删除现有的旧服务器密钥,您可以在 Google Cloud 控制台中执行此操作。

为 HTTP 请求提供授权

消息请求由两部分组成:HTTP 标头和 HTTP 正文。 HTTP 标头必须包含以下标头:

  • Authorization: key=YOUR_SERVER_KEY
    请确保这是服务器密钥,您可以在 Firebase 控制台设置窗格的 Cloud Messaging 标签页找到该密钥的值。 Android、Apple 平台和浏览器密钥会被 FCM 拒绝。
  • Content-Type: application/json(JSON 格式);application/x-www-form-urlencoded;charset=UTF-8(纯文本格式)。
    如果省略 Content-Type,则视为纯文本格式。

例如:

Content-Type:application/json
Authorization:key=AIzaSyZ-1u...0GBYzPu7Udno5aA

{
  "to" : "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
  "data" : {
    ...
  },
}

如需详细了解如何创建发送请求,请参阅构建发送请求旧版 HTTP 协议参考文档提供了您的消息可以包含的所有参数的列表。

检查服务器密钥的有效性

如果您在发送消息时收到身份验证错误,请检查服务器密钥的有效性。例如,在 Linux 上运行以下命令:

api_key=YOUR_SERVER_KEY

curl --header "Authorization: key=$api_key" \
     --header Content-Type:"application/json" \
     https://fcm.googleapis.com/fcm/send \
     -d "{\"registration_ids\":[\"ABC\"]}"

如果您收到 401 HTTP 状态代码,则表示您的服务器密钥无效。

为 XMPP 连接提供授权

使用 XMPP,您可以保持与 FCM 服务器之间的持久异步双向连接。该连接可用于在您的服务器与已连接到 FCM 的用户设备之间发送和接收消息。

您可以使用大多数 XMPP 库来管理与 FCM 之间的长期连接。XMPP 端点在 fcm-xmpp.googleapis.com:5235 上运行。针对非生产环境下的用户进行功能测试时,应改为连接到位于 fcm-xmpp.googleapis.com:5236 的测试用服务器(注意端口的不同)。

在测试环境(一个运行最新 FCM 版本的较小的环境)中进行常规测试有助于将真实用户与测试代码隔离。连接到 fcm-xmpp.googleapis.com:5236 的测试设备和测试代码应使用不同的 FCM 发送者 ID,以避免向正式使用环境中的用户发送测试消息,或通过测试连接发送来自正式使用环境流量的上行消息。

连接需要符合两个重要条件:

  • 您必须启动传输层安全协议 (TLS) 连接。请注意,FCM 目前不支持 STARTTLS 扩展
  • FCM 需要一种使用 <your_FCM_Sender_Id>@fcm.googleapis.com(FCM 发送者 ID)并以服务器密钥作为密码的 SASL PLAIN 身份验证机制。您可以在 Firebase 控制台设置窗格的 Cloud Messaging 标签页找到这些值。

无论什么时候连接失败,您都应立即重新连接。如果身份验证后连接断开,无需退避。FCM 允许每个发送者 ID 有 2500 个并行连接。

以下这段代码说明了如何为与 FCM 的 XMPP 连接进行身份验证以及提供授权。

XMPP 服务器

XMPP 服务器请求连接到 FCM

<stream:stream to="fcm.googleapis.com"
        version="1.0" xmlns="jabber:client"
        xmlns:stream="http://etherx.jabber.org/streams">

FCM

FCM 打开连接并请求身份验证机制,包括 PLAIN 方法。

<stream:features>
  <mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
    <mechanism>X-OAUTH2</mechanism>
    <mechanism>X-GOOGLE-TOKEN</mechanism>
    <mechanism>PLAIN</mechanism>
  </mechanisms>
</stream:features>

XMPP 服务器

XMPP 服务器必须使用 PLAIN 身份验证方法进行响应,并提供来自 Firebase 控制台设置窗格的 Cloud Messaging 标签页的服务器密钥。

<auth mechanism="PLAIN"
xmlns="urn:ietf:params:xml:ns:xmpp-sasl">MTI2MjAwMzQ3OTMzQHByb2plY3RzLmdjbS5hb
mFTeUIzcmNaTmtmbnFLZEZiOW1oekNCaVlwT1JEQTJKV1d0dw==</auth>

FCM

<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>

XMPP 服务器

<stream:stream to="fcm.googleapis.com"
        version="1.0" xmlns="jabber:client"
        xmlns:stream="http://etherx.jabber.org/streams">

FCM

<stream:features>
  <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/>
  <session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>
</stream:features>

XMPP 服务器

<iq type="set">
  <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"></bind>
</iq>

FCM

<iq type="result">
  <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">
    <jid>SENDER_ID@fcm.googleapis.com/RESOURCE</jid>
  </bind>
</iq>

注意:FCM 在传递消息时不使用绑定的资源。

如需详细了解如何创建发送请求,请参阅构建发送请求旧版 XMPP 协议参考提供了您的消息可以包含的所有参数的列表。