Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

向发送请求提供授权

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

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

向 HTTP v1 发送请求提供授权

根据服务器环境的详细信息,您可以组合使用以下策略对服务器向 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 = ServiceAccountCredentials.from_json_keyfile_name(
      'service-account.json', SCOPES)
  access_token_info = credentials.get_access_token()
  return access_token_info.access_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 Console 中执行此操作。

向 HTTP 请求提供授权

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

  • Authorization: key=YOUR_SERVER_KEY
    请确保这是服务器密钥,您可以在 Firebase 控制台设置窗格的 Cloud Messaging 标签页找到该密钥的值。 Android 密钥、iOS 密钥和浏览器密钥会被 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 协议参考提供了您的消息可以包含的所有参数的列表。