我有一个 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。”, “状态”:“未经身份验证” } 块引用
***** 2023 更新 ***** FCM http Legacy 已正式弃用,并将于 2024 年 6 月完全删除。任何使用 http Legacy 版本的人都应迁移到 V1。下面的示例使用 V1 而不是 http Legacy 版本:)
对于仍在寻找此问题答案的人 (2021),为了通过您自己的 PHP 系统向 Firebase 消息系统发送推送消息,您需要来自 Google Credentials 的访问令牌。以下是如何做到这一点 - 请注意我只在 PHP Laravel 中完成了此操作,而不是原始 PHP。但是您应该能够通过修改步骤来找到适合此问题的普通 PHP 解决方案(也与 Code Igniter 和其他 PHP 库相同)
在您的 http://console.firebase.google.com 中,在项目 -> 设置 -> 服务帐户下找到 Firebase 服务帐户。生成新的私钥并下载 json 文件。将其存储在您的服务器上用户无法访问的地方。
安装 Google API 客户端。对于 Laravel 来说是:
composer require google/apiclient --with-all-dependencies
打开composer.json,添加到autoload数组中。对于 Laravel 来说是:
"classmap": [
"vendor/google/apiclient/src/Google"
],
创建一个新的服务类(或者如果是普通 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'];
}
现在创建一个方法,通过 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++。
对于任何在不使用外部库或包的情况下寻找答案的人。此代码基于谷歌的此文档: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 小时。
@james 提供了非常有用且清晰的答案。您可以按照他的指导或 对于纯php 简单
composer require google/apiclient
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'];
首先你需要智威汤逊!
您可以在这里找到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"
)
)
);
对于 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");