從舊版 FCM API 遷移到 HTTP v1

使用已棄用的 HTTP 和 XMPP FCM 舊版 API 的應用程式應儘早遷移到 HTTP v1 API。使用這些 API 發送訊息(包括上游訊息)已於 2023 年 6 月 20 日棄用,並將於 2024 年 6 月刪除

除了持續的支援和新功能之外,HTTP v1 API 相對於舊版 API 還具有以下優勢:

  • 透過存取令牌提高安全性HTTP v1 API 根據 OAuth2 安全模型使用短期存取權杖。如果存取令牌公開,它只能被惡意使用一個小時左右,然後就會過期。刷新令牌的傳輸頻率不像舊 API 中使用的安全金鑰那麼頻繁,因此它們被捕獲的可能性要小得多。

  • 跨平台更有效率地自訂訊息對於訊息正文,HTTP v1 API 具有可存取所有目標實例的通用金鑰,以及可讓您跨平台自訂訊息的特定於平台的金鑰。這允許您建立“覆蓋”,在單一訊息中將略有不同的有效負載傳送到不同的客戶端平台。

  • 新客戶端平台版本的可擴展性更強且面向未來HTTP v1 API 完全支援 Apple 平台、Android 和 Web 上提供的訊息傳遞選項。由於每個平台在 JSON 有效負載中都有自己定義的區塊,因此 FCM 可以根據需要將 API 擴展到新版本和新平台。

更新伺服器端點

HTTP v1 API 的端點 URL 與舊端點的差異如下:

  • 它是版本化的,路徑中帶有/v1
  • 此路徑包含您的套用的 Firebase 專案的專案 ID,格式為/projects/myproject-ID/ 。此 ID 可在 Firebase 控制台的常規項目設定標籤中找到。
  • 它明確指定send方法為:send

若要更新 HTTP v1 的伺服器端點,請將這些元素新增至傳送請求標頭中的端點。

之前的 HTTP 請求

POST https://fcm.googleapis.com/fcm/send

之前的 XMPP 請求

舊版 XMPP 訊息透過連線傳送到以下端點:

fcm-xmpp.googleapis.com:5235

POST https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send

更新發送請求的授權

HTTP v1 傳送請求需要 OAuth 2.0 存取令牌,而不是舊請求中使用的伺服器金鑰字串。如果您使用 Admin SDK 傳送訊息,該程式庫會為您處理令牌。如果您使用的是原始協議,請按照本節中所述獲取令牌,並將其新增至標頭中,格式為Authorization: Bearer <valid Oauth 2.0 token>

Authorization: key=AIzaSyZ-1u...0GBYzPu7Udno5aA

Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA

根據伺服器環境的詳細信息,使用以下策略的組合來授權對 Firebase 服務的伺服器請求:

  • Google 應用程式預設憑證 (ADC)
  • 服務帳戶 JSON 文件
  • 從服務帳戶派生的短暫 OAuth 2.0 存取令牌

如果您的應用程式在 Compute Engine、Google Kubernetes Engine、App Engine 或 Cloud Functions(包括 Cloud Functions for Firebase)上執行,請使用應用程式預設憑證 (ADC)。 ADC 使用您現有的預設服務帳戶取得憑證來授權請求,並且 ADC 透過環境變數GOOGLE_APPLICATION_CREDENTIALS啟用靈活的本機測試。為了實現授權流程的最完全自動化,請將 ADC 與 Admin SDK 伺服器庫一起使用。

如果您的應用程式在非 Google 伺服器環境上執行,則需要從 Firebase 專案下載服務帳戶 JSON 檔案。只要您有權存取包含私鑰檔案的檔案系統,就可以使用環境變數GOOGLE_APPLICATION_CREDENTIALS透過這些手動取得的憑證來授權請求。如果您缺乏此類文件存取權限,則必須在程式碼中引用服務帳戶文件 - 由於存在暴露憑證的風險,因此應格外小心。

使用 ADC 提供憑證

Google 應用程式預設憑證 (ADC) 會依照下列順序檢查您的憑證:

  1. ADC 檢查環境變數GOOGLE_APPLICATION_CREDENTIALS是否已設定。如果設定了該變量,ADC 將使用該變數指向的服務帳戶檔案。

  2. 如果未設定環境變量,ADC 將使用 Compute Engine、Google Kubernetes Engine、App Engine 和 Cloud Functions 為在這些服務上執行的應用程式提供的預設服務帳戶。

  3. 如果 ADC 無法使用上述任一憑證,系統將會引發錯誤。

以下 Admin SDK 程式碼範例說明了此策略。此範例未明確指定應用程式憑證。但是,只要設定了環境變量,或只要應用程式在 Compute Engine、Google Kubernetes Engine、App Engine 或 Cloud Functions 上執行,ADC 就能夠隱含地找到憑證。

Node.js

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

爪哇

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

FirebaseApp.initializeApp(options);

Python

default_app = firebase_admin.initialize_app()

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"

視窗

使用 PowerShell:

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

完成上述步驟後,應用程式預設憑證 (ADC) 能夠隱式確定您的憑證,從而允許您在非 Google 環境中測試或執行時使用服務帳戶憑證。

使用憑證建立存取令牌

將您的 Firebase 憑證與您的首選語言的Google Auth 庫一起使用,以檢索短期 OAuth 2.0 存取權令牌:

節點.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

爪哇

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

存取令牌過期後,會自動呼叫令牌刷新方法以檢索更新的存取令牌。

若要授權存取 FCM,請請求範圍https://www.googleapis.com/auth/firebase.messaging

若要將存取權杖新增至 HTTP 請求標頭:

將令牌新增為Authorization標頭的值,格式為Authorization: Bearer <access_token>

節點.js

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

Python

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

爪哇

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

更新發送請求的負載

FCM HTTP v1 在 JSON 訊息負載的結構中引入了重大變更。主要是,這些變更確保在不同客戶端平台上接收訊息時能夠正確處理訊息;此外,這些變更為您提供了額外的靈活性,可以自訂或「覆蓋」每個平台的訊息欄位。

除了檢查本節中的範例之外,請參閱跨平台自訂訊息並查看API 參考以熟悉 HTTP v1。

範例:簡單的通知訊息

以下是一個非常簡單的通知有效負載(僅包含titlebodydata欄位)的比較,展示了舊版有效負載和 HTTP v1 有效負載的根本差異。

{
  "to": "/topics/news",
  "notification": {
    "title": "Breaking News",
    "body": "New news story available."
  },
  "data": {
    "story_id": "story_12345"
  }
}

{
  "message": {
    "topic": "news",
    "notification": {
      "title": "Breaking News",
      "body": "New news story available."
    },
    "data": {
      "story_id": "story_12345"
    }
  }
}

範例:針對多個平台

為了實現多平台定位,舊版 API 在後端執行了覆蓋。相較之下,HTTP v1 提供了特定於平台的密鑰塊,使平台之間的任何差異對開發人員來說都是明確可見的。這允許您始終透過單一請求定位多個平台,如下例所示。

// Android
{
  "to": "/topics/news",
  "notification": {
    "title": "Breaking News",
    "body": "New news story available.",
    "click_action": "TOP_STORY_ACTIVITY"
  },
  "data": {
    "story_id": "story_12345"
  }
}
// Apple
{
  "to": "/topics/news",
  "notification": {
    "title": "Breaking News",
    "body": "New news story available.",
    "click_action": "HANDLE_BREAKING_NEWS"
  },
  "data": {
    "story_id": "story_12345"
  }
}

{
  "message": {
    "topic": "news",
    "notification": {
      "title": "Breaking News",
      "body": "New news story available."
    },
    "data": {
      "story_id": "story_12345"
    },
    "android": {
      "notification": {
        "click_action": "TOP_STORY_ACTIVITY"
      }
    },
    "apns": {
      "payload": {
        "aps": {
          "category" : "NEW_MESSAGE_CATEGORY"
        }
      }
    }
  }
}

範例:使用平台覆蓋進行自訂

除了簡化訊息的跨平台定位之外,HTTP v1 API 還提供了根據平台自訂訊息的靈活性。

// Android
{
  "to": "/topics/news",
  "notification": {
    "title": "Breaking News",
    "body": "Check out the Top Story.",
    "click_action": "TOP_STORY_ACTIVITY"
  },
  "data": {
    "story_id": "story_12345"
  }
}
// Apple
{
  "to": "/topics/news",
  "notification": {
    "title": "Breaking News",
    "body": "New news story available.",
    "click_action": "HANDLE_BREAKING_NEWS"
  },
  "data": {
    "story_id": "story_12345"
  }
}

{
  "message": {
    "topic": "news",
    "notification": {
      "title": "Breaking News",
      "body": "New news story available."
    },
    "data": {
      "story_id": "story_12345"
    },
    "android": {
      "notification": {
        "click_action": "TOP_STORY_ACTIVITY",
        "body": "Check out the Top Story"
      }
    },
    "apns": {
      "payload": {
        "aps": {
          "category" : "NEW_MESSAGE_CATEGORY"
        }
      }
    }
  }
}

範例:針對特定設備

若要使用 HTTP v1 API 定位特定設備,請在token鍵(而非to鍵)中提供裝置的目前註冊令牌。

  { "notification": {
      "body": "This is an FCM notification message!",
      "time": "FCM Message"
    },
    "to" : "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1..."
  }

{
   "message":{
      "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
      "notification":{
        "body":"This is an FCM notification message!",
        "title":"FCM Message"
      }
   }
}

有關 FCM HTTP v1 API 的更多範例和信息,請參閱以下內容: