Firebase-FCM服務端開發

FCM 雲消息傳遞(Firebase Cloud Messaging)是一種免費的跨平臺消息傳遞解決方案,可以讓您向客戶端應用程序發送推送消息。使用FCM,您能夠將推送通知發送到單個設備,設備組或訂閱特定「主題」的設備。android

FCM特徵

消息類型

使用 FCM,您能夠向客戶端發送兩種類型的消息:golang

  • 通知消息,有時被視爲「顯示消息」。此類消息由 FCM SDK 自動處理。
  • 數據消息,由客戶端應用處理。

應用在後臺運行時,通知消息將被傳遞至通知面板。應用在前臺運行時,消息由回調函數處理。web

接收同時包含通知和數據有效負載的消息時的應用行爲取決於應用是在後臺仍是前臺運行 - 特別是在接收時應用是否處於活動狀態。算法

  • 在後臺運行時,應用會在通知面板中接收通知有效負載,且僅在用戶點按通知時處理數據有效負載。
  • 在前臺運行時,您的應用將會接收一個消息對象,且兩種有效負載均可用。

如下是包含 notification 鍵和 data 鍵的 JSON 格式的消息:編程

{
  "message":{
    "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "notification":{
      "title":"Portugal vs. Denmark",
      "body":"great match!"
    },
    "data" : {
      "Nick" : "Mario",
      "Room" : "PortugalVSDenmark"
    }
  }
}

什麼時候使用針對具體平臺的字段

若是須要執行下列操做,請使用針對具體平臺的字段:json

  • 僅向特定平臺發送字段
  • 發送針對具體平臺的字段以及通用字段

每當您想僅向特定平臺發送值時,請不要使用通用字段,而是使用針對具體平臺的字段。例如,要僅向 iOS 和網頁發送通知而不向 Android 發送通知,您必須針對 iOS 和網頁各使用一組字段。後端

示例:包含針對具體平臺的遞送選項的通知消息api

如下 v1 發送請求會向全部平臺發送通用的通知標題和內容,但也會發送一些針對具體平臺的覆蓋內容。具體而言,該請求會:緩存

  • 爲 Android 和網頁平臺設置較長的存留時間,同時將 APNs (iOS) 消息設置爲低優先級
  • 設置適當的鍵來定義用戶在 Android 和 iOS 上點按相應通知的結果(分別是 click_action 和 category)。
{
  "message":{
     "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
     "notification":{
       "title":"Match update",
       "body":"Arsenal goal in added time, score is now 3-0"
     },
     "android":{
       "ttl":"86400s",
       "notification"{
         "click_action":"OPEN_ACTIVITY_1"
       }
     },
     "apns": {
       "headers": {
         "apns-priority": "5",
       },
       "payload": {
         "aps": {
           "category": "NEW_MESSAGE_CATEGORY"
         }
       }
     },
     "webpush":{
       "headers":{
         "TTL":"86400"
       }
     }
   }
 }

限制和擴縮

咱們的目標是始終提供經過 FCM 發送的每條消息。可是,傳遞每條消息有時會致使總體用戶體驗不佳。在其餘狀況下,咱們須要提供邊界以確保 FCM 爲全部發送者提供可擴縮的服務。安全

向單一設備發送消息的最大速率
您能夠向單一設備發送最多 240 條消息/分鐘和 5000 條消息/小時。這個高閾值意味着容許短時間的流量突發,例如當用戶經過聊天快速互動時。此限制可防止發送邏輯時發生的錯誤無心中耗盡設備上的電池電量。

警告:不要按期發送接近此最大速率的消息。這可能會浪費最終用戶的資源,而且您的應用可能會被標記爲濫用。

上行消息限額
咱們將每一個項目的上行消息限制爲 15000 條/分鐘,以免上行目標服務器過載。

咱們將每臺設備的上行消息限制爲 1000 條/分鐘,以防止因不良應用行爲致使電池電量耗盡。

主題消息限額
主題訂閱添加/移除率限制爲每一個項目 3000 QPS。

咱們將每一個項目正在進行的主題扇出數量限制爲 1000。以後,咱們可能會拒絕其餘扇出請求,直到某些扇出完成爲止。

實際可實現的主題扇出率受同時請求扇出的項目數量的影響。單個項目的扇出率爲 10000 QPS 並不罕見,但這個數字不是一項保證,而是系統總負載的結果。值得注意的是,可用的扇出容量在項目之間劃分,而不是在扇出請求之間劃分。所以,若是您的項目有兩個正在進行的扇出,那麼每一個扇出只能確保可用扇出率的一半。最大化扇出速度的推薦方法是一次只有一個進行中的活動扇出。

FCM 服務端環境要求

應用服務器或受信任的服務器環境向 FCM 後端發送消息請求,而後 FCM 後端再將消息發送到用戶設備上運行的客戶端應用。

受信任的服務器環境的要求
您的應用服務器環境必須符合如下條件:

  • 可以向 FCM 後端發送格式正確的消息請求。
  • 可以使用指數退避算法處理請求和從新發送請求。
  • 可以安全地存儲服務器受權憑據和客戶端註冊令牌。
  • 對於 XMPP 協議(若是使用),服務器必須可以生成消息 ID 來惟一標識它發送的每條消息(FCM HTTP 後端會生成消息 ID 並在響應時返回這些 ID)。XMPP 消息 ID 對於每一個發送者 ID 而言都應是惟一的。

建議使用 Firebase Admin SDK,由於它支持各類流行的編程語言,並且處理身份驗證和受權的方法很是便捷。
Admin FCM API 可處理後端身份驗證工做,同時便於發送消息和管理主題訂閱。使用 Firebase Admin SDK,您能夠執行如下操做:

  • 向個別設備發送消息
  • 向主題和與一個或多個主題匹配的條件語句發送消息。
  • 爲設備訂閱和退訂主題
  • 針對不一樣目標平臺構建量身定製的消息負載。

FCM 服務器協議

目前 FCM 提供如下原始服務器協議:

  • FCM HTTP v1 API
  • 舊版 HTTP 協議
  • 舊版 XMPP 協議

您的應用服務器能夠分開使用或同時使用這些協議。由於在向多個平臺發送消息方面 FCM HTTP v1 API 是最新、最靈活的協議,所以推薦儘量使用此協議。若是您須要從設備到服務器的上行消息傳遞功能,則需實現 XMPP 協議。

XMPP 消息傳遞與 HTTP 消息傳遞具備如下差別:

  • 上行/下行消息

    • HTTP:僅下行,即雲到設備。
    • XMPP:上行和下行(即設備到雲、雲到設備)。
  • 消息傳遞(同步或異步)

    • HTTP:同步。應用服務器以 HTTP POST 請求的形式發送消息,並等待響應。此機制是同步的,且發送者沒法在收到響應前發送其餘消息。
    • XMPP:異步。應用服務器經過持續型 XMPP 鏈接,以全線速向/從全部設備發送/接收消息。XMPP 鏈接服務器異步發送確認或失敗通知(以 ACK 和 NACK JSON 編碼的特殊 XMPP 消息形式)。
  • JSON

    • HTTP:JSON 消息以 HTTP POST 的形式發送。
    • XMPP:JSON 消息封裝在 XMPP 消息中。
  • 純文本

    • HTTP:純文本消息以 HTTP POST 的形式發送。
    • XMPP:不支持。
  • 向多個註冊令牌發送多播下行消息。

    • HTTP:支持 JSON 格式的消息。
    • XMPP:不支持。
以上信息均摘自官網,更多信息請見官網。

FCM 工做流

Overview

以下圖所示,FCM充當消息發送者和客戶端之間的中介。客戶端應用程序是在設備上運行的啓用FCM的應用程序。應用服務器(由您或您的公司提供)是客戶端應用經過FCM與之通訊的啓用FCM的服務器。與GCM不一樣,FCM使您能夠經過Firebase控制檯通知GUI直接向客戶端應用程序發送消息
clipboard.png

Registration with FCM

客戶端應用必須首先向FCM註冊,而後才能進行消息傳遞。客戶端應用程序必須完成下圖中顯示的註冊步驟:
clipboard.png

  • 客戶端應用程序聯繫FCM以獲取註冊令牌,將發件人ID,API密鑰和App ID傳遞給FCM。
  • FCM將註冊令牌返回給客戶端應用程序。
  • 客戶端應用程序(可選)將註冊令牌轉發到應用服務器。

應用程序服務器緩存註冊令牌,以便與客戶端應用程序進行後續通訊。應用服務器能夠向客戶端應用程序發送確認,以指示已收到註冊令牌。在進行此握手以後,客戶端應用程序能夠從應用服務器接收消息(或向其發送消息)。若是舊令牌被泄露,則客戶端應用程序可能會收到新的註冊令牌(有關應用程序如何接收註冊令牌更新的示例,請參閱使用FCM進行遠程通知)。

Downstream messaging

下圖說明了Firebase Cloud Messaging如何存儲和轉發下游消息:
clipboard.png

當應用服務器向客戶端應用程序發送下游消息時,它將使用上圖中枚舉的如下步驟:

  • 應用服務器將消息發送到FCM。
  • 若是客戶端設備不可用,則FCM服務器將消息存儲在隊列中以供稍後傳輸。消息最多保留在FCM存儲中4周(有關詳細信息,請參閱設置消息的生命週期)。
  • 當客戶端設備可用時,FCM會將消息轉發到該設備上的客戶端應用程序。
  • 客戶端應用程序從FCM接收消息,對其進行處理,並將其顯示給用戶。例如,若是消息是遠程通知,則將其呈現給通知區域中的用戶。
  • 在此消息傳遞方案中(應用服務器將消息發送到單個客戶端應用程序),消息的長度最多可達4kB。

Topic messaging

主題消息使應用服務器能夠向已選擇加入特定主題的多個設備發送消息。您還能夠經過Firebase控制檯通知GUI撰寫和發送主題消息。 FCM處理主題消息到訂閱客戶端的路由和傳遞。此功能可用於天氣警報,股票報價和標題新聞等消息。
clipboard.png

主題消息中使用如下步驟(在客戶端應用程序獲取註冊令牌以後,如前所述)

  • 客戶端應用程序經過向FCM發送訂閱消息來訂閱主題。
  • 應用服務器將主題消息發送到FCM以進行分發。
  • FCM將主題消息轉發給已訂閱該主題的客戶端。

正在傳遞的消息

當下遊消息從應用服務器發送到客戶端應用程序時,應用服務器將消息發送到Google提供的FCM鏈接服務器;反過來,FCM鏈接服務器將消息轉發到運行客戶端應用程序的設備。消息能夠經過HTTP或XMPP(可擴展消息傳遞和在線協議)發送。因爲客戶端應用程序並不是始終鏈接或正在運行,所以FCM鏈接服務器會將消息排入並存儲,並在從新鏈接並可用時將其發送到客戶端應用程序。一樣,若是應用服務器不可用,FCM會將客戶端應用程序的上游消息排入應用服務器。

FCM使用如下憑據來標識應用服務器和客戶端應用,並使用這些憑據經過FCM受權消息事務:

Sender ID – 發件人ID是您在建立Firebase項目時分配的惟一數字值。發件人ID用於標識能夠向客戶端應用程序發送郵件的每一個應用程序服務器。發件人ID也是您的項目編號;註冊項目時,您能夠從Firebase控制檯獲取發件人ID。發件人ID的示例是496915549731

API Key – API密鑰使應用服務器能夠訪問Firebase服務; FCM使用此密鑰對應用服務器進行身份驗證。此憑證也稱爲服務器密鑰或Web API密鑰。 API密鑰的示例是AJzbSyCTcpfRT1YRqbz-jIwp1h06YdauvewGDzk。

App ID – 您的客戶端應用程序的標識(獨立於任何給定的設備)註冊以接收來自FCM的消息。應用程序ID的示例是1:415712510732:android:0e1eb7a661af2460

Registration Token – 註冊令牌(也稱爲實例ID)是給定設備上客戶端應用程序的FCM身份。註冊令牌在運行時生成 - 您的應用程序在設備上運行時首次向FCM註冊時會收到註冊令牌。註冊令牌受權客戶端應用程序的實例(在該特定設備上運行)以從FCM接收消息。註冊令牌的示例是fkBQTHxKKhs:AP91bHuEedxM4xFAUn0z ... JKZS(很是長的字符串)。

設計與實現

問題分析

在基於FCM開發時最關鍵的問題是要搞清楚你指望的是啥?有哪些安全問題須要考慮。好比:

  • 在同一個設備,同一個應用,有多個帳號切換登陸時,會出現什麼狀況?在這種場景下你指望的正確策略是什麼?
  • 用同一個帳號,在多個設備上登陸應用,會出現什麼狀況?在這種場景下你指望的正確策略又是什麼?
  • 客戶端向服務端註冊Token時有漏洞嗎?如何確保安全性?

現假設有A,B 2個帳號,若是A帳號登陸APP後發表了一篇文章,而後A從APP內退出了。假設有人收藏了該文章,那麼A會收到通知嗎?你指望A收到通知嗎?若是這時候再切換B帳號呢?

好了,這裏面有太多種結果了,不作一一分析了,最主要的是想清楚你要的是什麼?你指望的是什麼?什麼是正確且合理的?說下我指望的:

我指望當「A」從「APP內」退出時,再也不收到與「A」帳號有關的私有通知信息。可是像一些可有可無的,通用型的,非隱私的消息能夠接收,如:系統公告類的通知等。

若是要達到這個指望應該如何設計呢?指望值明確了,需求明確了就逐個分析唄。

設備、註冊Token、用戶帳號、狀態,這些是關鍵元素。其中狀態的值爲:online和offline。

  • online 指用戶登陸了APP,且APP在設備可用。
  • offline 指用戶退出了APP,且APP在設備可用。

邏輯代碼

登陸

「帳號A」在登陸後,向「APP服務端」註冊Token。
「服務端-註冊Token接口」一個設備,只容許存在一條記錄,由於一個設備只能同時登陸一個帳號。因此在註冊時,把設備做爲查詢條件,若是存在,更新uid和Token,並設置狀態爲online;不然,插入用戶ID、設備、Token,並設置狀態爲online,用戶ID從Http Header中的LoginToken裏解析出來,設備信息也從Http Header中獲取。

走Topic發送消息,優勢是同一個帳號,多個設備登陸的狀況比較方便,缺點是退出後不能收到如何消息

「服務端」建立Topic,以uid做爲Topic名稱。
「服務端」訂閱Topic,以uid對應的Tokens做爲訂閱者,訂閱Topic。
「服務端」發送消息到Topic,Topic名稱爲uid。
「FCM」會把通知消息Push到Topic的訂閱者。

走Token發送消息

「服務端」根據帳號,找到全部online狀態的Tokens,發送消息到Tokens。

接收

「客戶端」收到通知消息,若是同一個帳號多個設備的狀況,那麼每一個設備都會收到通知。

退出

「帳號A」在退出時向「服務端」註銷Token,退出前獲取註冊Token,而後向「服務端」發送註銷Token請求。
「服務端」根據帳號和設備,修改Token狀態爲offline。
「服務端」取消訂閱Topic。

切換帳號

「帳號B」退出A,登陸B時,因爲一個設備只容許容許一個帳號,因此B會覆蓋A的註冊信息。

以上實現既知足「同一個設備,同一個應用,多個帳號切換的狀況」又知足「同一個帳號,多個設備的狀況」。

Firebase設備通知推送原則

當應用在前臺或系統後臺運行時,會接收到Firebase推送給該應用的全部通知。不然當應用不可用,而後從新啓動時,會且只能收到Firebase推送的最後一條通知。爲何呢?在我看來這是合理的,設備通知不一樣於站內消息,不可能把應用完全離線的這段時間所產生的設備通知,在應用從新打開時一次所有推送給設備。另外即便沒有設備通知還有站內消息呢,錯過的通知在站內消息裏都能找到。

數據結構

FCM渠道表「notify_fcm」
表結構以下:

id: {type: 'integer', primaryKey: true, autoIncrement:true} 
device: {type: 'string', required: true} //設備號,Agent;
token: {type: 'string', required: true} //FCM生成的註冊Token;
userId: {type: 'string', required: true},//用戶ID;
state: {type: 'integer', required: true} //狀態,online|offline;
createdAt: {type: 'timestamp', required: true} //建立時間;
updateAt: {type: 'timestamp', required: true} //更新時間;

Go實例

package main

import (
    "context"
    "fmt"
    "log"

    firebase "firebase.google.com/go"
    "firebase.google.com/go/messaging"
    "google.golang.org/api/option"
)

func main() {
    var serviceAccountKey = []byte(`{
        "type": "service_account",
        "project_id": "lvxiaorun-22c97",
        "private_key_id": "a212612afee74a54e9e30cb2bfbc5b0c118ca3cc",
        "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCpieVk85eBv9Eh\nGktoEf7rv+lz7PQh6CTa4F2QtljaFM3k3hv4v3F+JYnVhWKYL1yz7D4vNgByv3lD\nsXGRMwe1uK5pP5DLke7X1q0RQJn928QrUKDK71MKqDPTeYMjZEoVLdFRbVzbvpf9\nwks6H+vh9CFGwJ4D/H/7tX//bRR/f6ape5J+VwEjWH0itLXd35klh3oqWCA4/Dwg\nxNg7LzejVdM+GOhyuvLCZfEOcno8wF+YI8Uj7Ut27Wj9fLcO2cW2ybaK8Y87CfKO\nKmQgSt1lqgbrksi+fe725BxbXZp/eF9MrS8CPLOWgv/DRW3aP0ocgxx6TX0z8K1a\ncFdvXwcxAgMBAAECggEABgfe9liY6NdlLceE66qKNiIhQIurAoLCvttw0Js/7WAE\nk/HXrmFG/P0CWmtQfsfehRLwAldqLCrF+kO3XboiOdNcNue5M5iZFanwBZdV8vsM\njxri4V0ih9REZa8inFFutjKnSb15amKs/uyYpvRoPGUmAuGKrWsftVk3OKONcVyP\nA2L8N/keTc8IWKe4GlIdfoU2f/hAxbVYiu0u2HXgdGlFmY1vpOrFgHbJZcXyUlhg\nQvtgCnt/bwKr3GkuWXJff9pm4wf7A2e73jv68zuucCV5P9iExVIAZIkLvhlxgqn1\ncKF7Ib5K/RVzdXgcVNpL0V6ewv+IC1fjHKLJYbXiDQKBgQDSbOXnxzb639NOIlvJ\nSb0wpTJVmWLWsIvJjO74K2ZNOQ38f7LDuvZ1/M2W0QsvVOw43Gpqijza0HV0aCRH\nqrPldgTRBJAqY/VexWt/kuOvrCA1IkXRl8jayLoMrGxCv12fiIp8ryDbikCuVDCE\n9VuenZGbe1++O5aBjMG/3c6sxQKBgQDOQgVAiAiTmaiUv9NPIX5sfpuV0U7QB6TT\n83wS45ahheanWf/G2CvidazK9KR5oEquljVGTPsmK01x17/1pnM+o6p84ZETNQ12\n4SsJpT7NxId/Vkw2Hmn/qzhHZa1wckdEPrHc70Upre/YJ6snp+brCBQNiuhALZde\nc6yrHZKvfQKBgQCNvlE3ydfdMjxyW26crpFEXWMEiigsGgxvngGzJfjpd89WEObo\nNd6jJ8GNIA96uKfOvZrpXWkUtGsKGMSnifNYVCF2cq5x/5dfWXjKHLZGtZmUcRu6\nzZW82o2Iz/S1GZcFScKPrqBhgkWDqK5uQaCPvfBBXd/mktkVNy2kAtOfSQKBgQC+\nx6xp+ynLtOaM6D4BRJ7Wpektk5QNsfRRJDdQlXi/4MXvZ7zBZTR6XJQ+ijkUUyKh\nCEkwxIXN0WHp+kERbCvO9b39kvsIxBq3KiEP4+wKkk0uiFkn+cvb87izuaXKi7nF\nsyP7ksnrenqN+mtC2/goz6kUubaHnmQTtnUxNcJ3VQKBgQDDbVOQx1LCex9HHAV4\n2Rula9E27w0ujYdhf3YvQ66mOG6Ig5+1YhiXrKrElfCCAVc7H8KmAt7iPHcJXKgC\nMJSJK7XWasj+Ld2SdbPT2LgwGglx907N+wyS47+vOh9Ppu3d5Tv9/BIKkvNKXBu+\ndQBw8Xw1P+U5QtIPf7LOKMtNEw==\n-----END PRIVATE KEY-----\n",
        "client_email": "firebase-adminsdk-k3958@lvxiaorun-22c97.iam.gserviceaccount.com",
        "client_id": "103134511880014813376",
        "auth_uri": "https://accounts.google.com/o/oauth2/auth",
        "token_uri": "https://oauth2.googleapis.com/token",
        "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
        "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-k3958%40lvxiaorun-22c97.iam.gserviceaccount.com"
     }`)

    // opt := option.WithCredentialsFile("path/to/serviceAccountKey.json")
    opt := option.WithCredentialsJSON(serviceAccountKey)
    app, err := firebase.NewApp(context.Background(), nil, opt)
    if err != nil {
        log.Fatalf("error initializing app: %v\n", err)
    }

    // Obtain a messaging.Client from the App.
    ctx := context.Background()
    client, err := app.Messaging(ctx)
    if err != nil {
        log.Fatalf("error getting Messaging client: %v\n", err)
    }

    uid := "25696773511053390"
    // This registration token comes from the client FCM SDKs.
    registrationToken := "fxV6ToLJh3A:APA91bGfcaOl4mmnj_mPY7MTscjzT0aZLvyK5xaLLboWavxFoeqc3hZu_npEtaINebzHAfOrARg4kn9RmWC9ZYKvhqJrPhnNI43qtUruQsvd7Or7w_ZnDG4agOMM_7xB0J4ci9UHPT5S"

    // These registration tokens come from the client FCM SDKs.
    registrationTokens := []string{
        registrationToken,
        // ...
        "cBYSJNhfG_Q:APA91bFzLxiSVynUc2thc6aGfF1ba_6WoJvOctw2_1cIlUEr2r7Pf-n_Qk6uisLpc9Whcf-UU4WwcjnRwLTm_Zok1pH2RGw2_WvLmaT_AdZp84caH29haB4gQFIdrc0wQSr-vVgR0F3o",
    }

    subscribe(ctx, client, uid, registrationTokens)
    // sendMsgToToken(ctx, client, registrationToken)
    sendMsgToTopic(ctx, client, uid)
    // createCustomToken(ctx, app)
}

func sendMsgToTopic(ctx context.Context, client *messaging.Client, topic string) {
    notification := &messaging.Notification{
        Title: "0007 2019-04-23",
        Body: "fffffffffffffffffffffffffff.",
    }

    // See documentation on defining a message payload.
    message := &messaging.Message{
        Data: map[string]string{
            "score": "88888",
            "time": "2:45",
        },
        Notification: notification,
        Topic: topic,
    }

    // Send a message to the devices subscribed to the provided topic.
    response, err := client.Send(ctx, message)
    if err != nil {
        log.Fatalln(err)
    }
    // Response is a message ID string.
    fmt.Println("Successfully sent message:", response)
}

func sendMsgToToken(ctx context.Context, client *messaging.Client, registrationToken string) {
    // See documentation on defining a message payload.

    notification := &messaging.Notification{
        Title: "$GOOG up 1.43% on the day",
        Body: "$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.",
    }

    // timestampMillis := int64(12345)

    message := &messaging.Message{
        // Data: map[string]string{
        //  "score": "850",
        //  "time": "2:45",
        // },
        Notification: notification,
        Webpush: &messaging.WebpushConfig{
            Notification: &messaging.WebpushNotification{
                Title: "title",
                Body: "body",
                //      Icon: "icon",
            },
            FcmOptions: &messaging.WebpushFcmOptions{
                Link: "https://fcm.googleapis.com/",
            },
        },
        Token: registrationToken,
    }

    // Send a message to the device corresponding to the provided
    // registration token.
    response, err := client.Send(ctx, message)
    if err != nil {
        log.Fatalln(err)
    }
    // Response is a message ID string.
    fmt.Println("Successfully sent message:", response)
}

func subscribe(ctx context.Context, client *messaging.Client, topic string, registrationTokens []string) {
    // Subscribe the devices corresponding to the registration tokens to the
    // topic.
    response, err := client.SubscribeToTopic(ctx, registrationTokens, topic)
    if err != nil {
        log.Fatalln(err)
    }
    // See the TopicManagementResponse reference documentation
    // for the contents of response.
    fmt.Println(response.SuccessCount, "tokens were subscribed successfully")
}

func createCustomToken(ctx context.Context, app *firebase.App) {
    authClient, err := app.Auth(context.Background())
    if err != nil {
        log.Fatalf("error getting Auth client: %v\n", err)
    }

    token, err := authClient.CustomToken(ctx, "25696773511053390")
    if err != nil {
        log.Fatalf("error minting custom token: %v\n", err)
    }

    log.Printf("Got custom token: %v\n", token)
}

參考

Firebase Cloud Messaging
Device Group Management With Firebase Cloud Messaging
Firebase 雲消息傳送

相關文章
相關標籤/搜索