为 Firebase 云消息传递 PHP 生成 OAUTH 令牌

问题描述 投票:0回答:5

我有一个 PHP 页面,我用它来向我开发的移动应用程序的用户发送通知,该页面直到上个月都工作正常,然后它给了我这个错误

{"multicast_id":5174063503598899354,"成功":0,"失败":1,"canonical_ids":0,"结果":[{"错误":"无效注册"}]}

我尝试使用此链接中的文档生成 OAUTH 令牌 https://firebase.google.com/docs/cloud-messaging/auth-server#node.js 但它需要 NODE.JS 服务器,而我的服务器不支持 Node.Js ,我尝试使用 Firebase Admin SDK 但找不到任何内容。 这是页面的PHP代码

<?php

//Includes the file that contains your project's unique server key from the Firebase Console.
require_once("serverKeyInfo.php");

//Sets the serverKey variable to the googleServerKey variable in the serverKeyInfo.php script.
$serverKey = $googleServerKey;

//URL that we will send our message to for it to be processed by Firebase.
    $url = "https://fcm.googleapis.com/fcm/send";

//Recipient of the message. This can be a device token (to send to an individual device) 
//or a topic (to be sent to all devices subscribed to the specified topic).
$recipient = $_POST['rec'];

//Structure of our notification that will be displayed on the user's screen if the app is in the background.
$notification =array(
    'title'   => $_POST['title'],
    'body'   => $_POST['body'],
    'sound' => 'default'
);

//Structure of the data that will be sent with the message but not visible to the user.
//We can however use Unity to access this data.
$dataPayload =array( 

    "powerLevel" => "9001",
    "dataString" => "This is some string data"
);

//Full structure of message inculding target device(s), notification, and data.
$fields =array(

    'to'  => $recipient,
    'notification' => $notification,
    'data' => $dataPayload
);

//Set the appropriate headers
$headers = array(

'Authorization: key=' . $serverKey,
'Content-Type: application/json'
);
//Send the message using cURL.
$ch = curl_init();
curl_setopt( $ch,CURLOPT_URL, $url);
curl_setopt( $ch,CURLOPT_POST, true );
curl_setopt( $ch,CURLOPT_HTTPHEADER, $headers );
curl_setopt( $ch,CURLOPT_RETURNTRANSFER, true );
curl_setopt( $ch,CURLOPT_SSL_VERIFYPEER, false );
curl_setopt( $ch,CURLOPT_POSTFIELDS, json_encode( $fields ) );
$result = curl_exec($ch );
curl_close( $ch );

//Result is printed to screen.
echo $result;
?>

任何人都可以给我发送一个示例吗?我该怎么做(我是 PHP 初学者) 预先感谢

*更新:我还尝试将代码中的 $url 更改为

$url = "https://fcm.googleapis.com/v1/projects/notifications-9ccdd/messages:send";

但它给了我这个错误

“错误”:{ “代码”:401, "message": "请求的身份验证凭据无效。需要 OAuth 2 访问令牌、登录 cookie 或其他有效的 身份验证凭证。请参阅 https://developers.google.com/identity/sign-in/web/devconsole-project。”, “状态”:“未经身份验证” } 块引用

php firebase oauth firebase-cloud-messaging token
5个回答
23
投票

***** 2023 更新 ***** FCM http Legacy 已正式弃用,并将于 2024 年 6 月完全删除。任何使用 http Legacy 版本的人都应迁移到 V1。下面的示例使用 V1 而不是 http Legacy 版本:)


对于仍在寻找此问题答案的人 (2021),为了通过您自己的 PHP 系统向 Firebase 消息系统发送推送消息,您需要来自 Google Credentials 的访问令牌。以下是如何做到这一点 - 请注意我只在 PHP Laravel 中完成了此操作,而不是原始 PHP。但是您应该能够通过修改步骤来找到适合此问题的普通 PHP 解决方案(也与 Code Igniter 和其他 PHP 库相同)

  1. 在您的 http://console.firebase.google.com 中,在项目 -> 设置 -> 服务帐户下找到 Firebase 服务帐户。生成新的私钥并下载 json 文件。将其存储在您的服务器上用户无法访问的地方。

  2. 安装 Google API 客户端。对于 Laravel 来说是:

      composer require google/apiclient --with-all-dependencies
    
  3. 打开composer.json,添加到autoload数组中。对于 Laravel 来说是:

     "classmap": [
         "vendor/google/apiclient/src/Google"
     ],
    
  4. 创建一个新的服务类(或者如果是普通 PHP,则创建一个新的类),并添加以下方法来检索访问令牌:

    private function getGoogleAccessToken(){
    
         $credentialsFilePath = 'the-folder-and-filename-of-your-downloaded-service-account-file.json'; //replace this with your actual path and file name
         $client = new \Google_Client();
         $client->setAuthConfig($credentialsFilePath);
         $client->addScope('https://www.googleapis.com/auth/firebase.messaging');
         $client->refreshTokenWithAssertion();
         $token = $client->getAccessToken();
         return $token['access_token'];
    }
    
  5. 现在创建一个方法,通过 CURL 将所有消息信息发送到 Firebase:

    public function sendMessage(){
    
     $apiurl = 'https://fcm.googleapis.com/v1/projects/your-project-id/messages:send';   //replace "your-project-id" with...your project ID
    
     $headers = [
             'Authorization: Bearer ' . $this->getGoogleAccessToken(),
             'Content-Type: application/json'
     ];
    
     $notification_tray = [
             'title'             => "Some title",
             'body'              => "Some content",
         ];
    
     $in_app_module = [
             "title"          => "Some data title (optional)",
             "body"           => "Some data body (optional)",
         ];
     //The $in_app_module array above can be empty - I use this to send variables in to my app when it is opened, so the user sees a popup module with the message additional to the generic task tray notification.
    
      $message = [
            'message' => [
                 'notification'     => $notification_tray,
                 'data'             => $in_app_module,
             ],
      ];
    
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $apiurl);
      curl_setopt($ch, CURLOPT_POST, true);
      curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
      curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($message));
    
      $result = curl_exec($ch);
    
      if ($result === FALSE) {
          //Failed
          die('Curl failed: ' . curl_error($ch));
      }
    
      curl_close($ch);
    
    }
    

Google 建议您仅在无法直接将 JSON 文件作为环境变量添加到服务器上时才使用此方法。 我不知道为什么 Google 没有关于这个主题的 PHP 更好的文档,它似乎更喜欢 node.js 、 Go、 Java 和 C++。


19
投票

对于任何在不使用外部库或包的情况下寻找答案的人。此代码基于谷歌的此文档:https://developers.google.com/identity/protocols/oauth2/service-account#httprest

第 1 步:在 http://console.firebase.google.com 中的项目 -> 设置 -> 服务帐户下找到 Firebase 服务帐户。生成新的私钥并下载 json 文件。

// This function is needed, because php doesn't have support for base64UrlEncoded strings
function base64UrlEncode($text)
{
    return str_replace(
        ['+', '/', '='],
        ['-', '_', ''],
        base64_encode($text)
    );
}

// Read service account details
$authConfigString = file_get_contents("path_to_the_json_file_you_just_downloaded.json");

// Parse service account details
$authConfig = json_decode($authConfigString);

// Read private key from service account details
$secret = openssl_get_privatekey($authConfig->private_key);

// Create the token header
$header = json_encode([
    'typ' => 'JWT',
    'alg' => 'RS256'
]);

// Get seconds since 1 January 1970
$time = time();

// Allow 1 minute time deviation between client en server (not sure if this is necessary)
$start = $time - 60;
$end = $start + 3600;

// Create payload
$payload = json_encode([
    "iss" => $authConfig->client_email,
    "scope" => "https://www.googleapis.com/auth/firebase.messaging",
    "aud" => "https://oauth2.googleapis.com/token",
    "exp" => $end,
    "iat" => $start
]);

// Encode Header
$base64UrlHeader = base64UrlEncode($header);

// Encode Payload
$base64UrlPayload = base64UrlEncode($payload);

// Create Signature Hash
$result = openssl_sign($base64UrlHeader . "." . $base64UrlPayload, $signature, $secret, OPENSSL_ALGO_SHA256);

// Encode Signature to Base64Url String
$base64UrlSignature = base64UrlEncode($signature);

// Create JWT
$jwt = $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature;

//-----Request token, with an http post request------
$options = array('http' => array(
    'method'  => 'POST',
    'content' => 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion='.$jwt,
    'header'  => "Content-Type: application/x-www-form-urlencoded"
));
$context  = stream_context_create($options);
$responseText = file_get_contents("https://oauth2.googleapis.com/token", false, $context);

$response = json_decode($responseText);

响应有 3 个字段:

access_token
expires_in
token_type
。在对 firebase 的调用中使用 access_token 来验证您的调用。您还应该将这个access_token
expires_in
一起
存储
,并在其即将过期时请求新的令牌。令牌的最长生命周期为 1 小时。


7
投票

@james 提供了非常有用且清晰的答案。您可以按照他的指导或 对于纯php 简单

  1. 在您的 http://console.firebase.google.com 中的项目 -> 设置 -> 服务帐户下找到 Firebase 服务帐户。生成新的私钥并下载 json 文件。上传到您服务器上的某个位置
  2. 安装 Google API 客户端:
    composer require google/apiclient
  3. 在您的 PHP 文件中
require "./vendor/autoload.php";
$client= new Google_Client();
$client->setAuthConfig("path-downloaded-json-file.json");
$client->addScope('https://www.googleapis.com/auth/firebase.messaging');
$client->refreshTokenWithAssertion();
$token = $client->getAccessToken();
echo $token['access_token'];

0
投票

首先你需要智威汤逊!

您可以在这里找到JWT GitHub 的链接

创建访问令牌:

use Firebase\JWT\JWT;
use Firebase\JWT\Key;


require 'vendors/php-jwt-main/src/JWT.php';
require 'vendors/php-jwt-main/src/Key.php';

function returnFireBaseTkn(){
    $jsonInfo = json_decode(file_get_contents("YOUR JSON FILE WITH CREDENTIAL HERE"), true);

    $now_seconds = time();
    
    $privateKey = $jsonInfo['private_key'];
    
    $payload = [
        'iss' => $jsonInfo['client_email'],
        'scope' => 'https://www.googleapis.com/auth/firebase.messaging',
        'aud' => $jsonInfo['token_uri'],
        //Token to be expired after 1 hour
        'exp' => $now_seconds + (60 * 60),
        'iat' => $now_seconds
    ];
    
    $jwt = JWT::encode($payload, $privateKey, 'RS256');
    
    // create curl resource
    $ch = curl_init();
    
    // set post fields
    $post = [
        'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
        'assertion' => $jwt
    ];
    
    $ch = curl_init($jsonInfo['token_uri']);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
    
    // execute!
    $response = curl_exec($ch);
    
    // close the connection, release resources used
    curl_close($ch);
    
    // do anything you want with your response
    $jsonObj = json_decode($response, true);

    return $jsonObj['access_token'];
}

发送通知:

function sendNotif($info){

    $apiurl = 'https://fcm.googleapis.com/v1/projects/your-project-id/messages:send';

    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL, $apiurl);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($info));

    $headers = array(
    //It's normal to find .............................. on the access token!!
        'Authorization: Bearer ' . 'access_token_ID_here',
        'Content-Type: application/json'
    );

    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

    $result = curl_exec($ch);

    if (curl_errno($ch)) {
        echo 'Error:' . curl_error($ch);
    }
    curl_close($ch);
}

// Notification need to be in this format for single user!
$notification = array(
    "message" => array(
        "token" => "USER_ID",
        "notification" => array(
            "title" => "teste",
            "body" => "123"
        )
    )
);

sendNotif($notification);

如果您想用于一组用户:

$notification = array(
    "message" => array(
        "topic" => "GROUP_ID",
        "notification" => array(
            "title" => "teste",
            "body" => "123"
        )
    )
);

0
投票

对于 Larave 运行以下命令

sudo composer require google/apiclient

在模型中创建一个名为 getBearerToken 的函数,该函数将令牌在缓存中存储 55 分钟。您可以更改时间,但在我的例子中,令牌生命周期为 1 小时。

use Google\Client as GoogleClient;
use Carbon\Carbon;

private function getBearerToken(){
    try{
        $cache_key  = md5('google-auth-cache');
        $token      = \Cache::get($cache_key); 
        if(empty($token)){
            $serviceAccountPath = 'path/to/google/service-account.json';                  
            $client = new GoogleClient();
            $client->setAuthConfig($serviceAccountPath);
            $client->addScope('https://www.googleapis.com/auth/firebase.messaging');
            $client->fetchAccessTokenWithAssertion();
            $token = $client->getAccessToken();
            $expiresAt  = Carbon::now()->addMinutes(55);
            \Cache::put($cache_key, $token, $expiresAt); 
        }
        $accesstoken = isset($token['access_token']) ? $token['access_token'] : '';

        return $accesstoken;
    }
    catch(\Exception $e){
        echo sprintf('Line : %s, Message : %s', $e->getLine(), $e->getMessage());
    }
}    

现在您可以使用此功能发送通知了

$token = self::getBearerToken();
$headers = array("Authorization: Bearer $token", "Content-Type:application/json");
© www.soinside.com 2019 - 2024. All rights reserved.