You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 08:55:05