FCM从Legacy迁移至HTTP v1报错求助:Invalid JWT令牌问题
FCM HTTP v1迁移报错:invalid_grant(无效JWT)解决方案
核心错误原因
错误提示明确指出JWT令牌的iat(签发时间)和exp(过期时间)不在合理范围,最常见的原因是服务器系统时间与谷歌服务器时间偏差过大,其次是请求格式不符合FCM v1规范、Service Account配置错误。
分步解决方案
1. 同步服务器系统时间
JWT的有效性依赖精准的时间戳,若服务器时间与标准时间差超过几分钟,谷歌会判定令牌无效。
- 对于Linux服务器,执行NTP同步命令:
sudo apt-get install ntpdate sudo ntpdate pool.ntp.org - 配置自动时间同步(避免后续再次偏差):
sudo systemctl enable ntp sudo systemctl start ntp
2. 修正FCM v1请求格式
FCM HTTP v1的请求体要求必须包裹在message顶层字段中,你的代码缺少该结构,即使获取到有效token也会触发后续错误。修正后的请求体结构如下:
$apiBody = [ 'message' => [ 'notification' => $notifData, 'token' => $devicetoken ] ];
3. 优化AccessToken获取逻辑
避免每次推送都重新请求token(token有效期60分钟),添加缓存逻辑减少请求次数:
private static $cachedToken = null; private static $tokenExpiry = 0; function getGoogleAccessToken(){ $currentTime = time(); // 检查缓存token是否有效 if(self::$cachedToken && $currentTime < self::$tokenExpiry){ return self::$cachedToken; } $credentialsFilePath = 'service-account.json'; $client = new Google_Client(); $client->setAuthConfig($credentialsFilePath); $client->addScope('https://www.googleapis.com/auth/firebase.messaging'); $client->refreshTokenWithAssertion(); $token = $client->getAccessToken(); // 更新缓存和过期时间 self::$cachedToken = $token['access_token']; self::$tokenExpiry = $currentTime + $token['expires_in'] - 60; // 提前60秒刷新 return self::$cachedToken; }
4. 验证Service Account配置
- 确认
service-account.json是从Firebase控制台项目设置>服务帐号中下载的完整文件,未被修改; - 确保该服务帐号拥有
Firebase Cloud Messaging Admin角色(在IAM控制台配置)。
修正后的完整代码
<?php error_reporting(E_ALL); ini_set('display_errors', 1); require_once 'google-api-php-client--PHP7.4/vendor/autoload.php'; $title = "title"; $body = "body"; $devicetoken = "*****device_token****"; // 缓存token相关静态变量 private static $cachedToken = null; private static $tokenExpiry = 0; function getGoogleAccessToken(){ $currentTime = time(); if(self::$cachedToken && $currentTime < self::$tokenExpiry){ return self::$cachedToken; } $credentialsFilePath = 'service-account.json'; $client = new Google_Client(); $client->setAuthConfig($credentialsFilePath); $client->addScope('https://www.googleapis.com/auth/firebase.messaging'); $client->refreshTokenWithAssertion(); $token = $client->getAccessToken(); self::$cachedToken = $token['access_token']; self::$tokenExpiry = $currentTime + $token['expires_in'] - 60; return self::$cachedToken; } function sendFCM($title, $body, $devicetoken) { $url = 'https://fcm.googleapis.com/v1/projects/***project_name*****/messages:send'; $headers = array ( 'Authorization: Bearer ' . getGoogleAccessToken(), 'Content-Type:application/json' ); $notifData = [ 'title' => $title, 'body' => $body, 'click_action' => "OPEN_NOTIFY_PAGE" ]; // 修正FCM v1请求体结构 $apiBody = [ 'message' => [ 'notification' => $notifData, 'token' => $devicetoken ] ]; $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_POSTFIELDS, json_encode($apiBody)); $result = curl_exec($ch); print($result); curl_close($ch); return $result; } sendFCM($title, $body, $devicetoken); ?>
内容的提问来源于stack exchange,提问作者YP Work Stuff
相关产品推荐
相关产品推荐

