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

PHP 5.6下FCM迁移至HTTP v1协议及Google OAuth2令牌获取问题

问题背景

我正在将PHP 5.6应用重构为Java应用,预计6月完成。在此期间需要迁移推送通知确保正常发送,但原《Google App Engine PHP 5标准环境文档》(讲解Google OAuth2用户认证)已于1月30日终止,有两个核心问题:

  1. 如何在PHP 5.6版本中获取Google OAuth2令牌?
  2. PHP 5.6下如何通过FCM HTTP v1协议实现推送?

我通过GPT获取了一段代码,但不确定如何获取$refreshToken,也无法确认代码是否能正常运行:

function get_access_token($clientId, $clientSecret, $refreshToken) {
    $url = 'https://accounts.google.com/o/oauth2/token';
    $data = [
        'client_id' => $clientId,
        'client_secret' => $clientSecret,
        'refresh_token' => $refreshToken,
        'grant_type' => 'refresh_token',
    ];

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));

    $result = curl_exec($ch);
    curl_close($ch);

    $result = json_decode($result, true);

    if(array_key_exists('access_token', $result)) {
        return $result['access_token'];
    } else {
        return null;
    }
}

function send_notification_common($tokens, $message) {
    $clientId = 'MyClientId';
    $clientSecret = 'MyClientSecret';

    $url = 'https://fcm.googleapis.com/v1/projects/gymgym-4ecef/messages:send';
    $fields = array(
        'message' => array(
            'token' => $tokens,
            'notification' => $message,
            'data' => $message
        )
    );
    $headers = array(
        'Authorization: Bearer ' . get_access_token($clientId, $clientSecret, $refreshToken),
        'Content-Type: application/json'
    );

    $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_VERIFYHOST, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fields));
    $result = curl_exec($ch);
    if ($result === FALSE) {
        die('Curl failed: ' . curl_error($ch));
    }
    curl_close($ch);
    return $result;
}

我已按以下步骤获取了clientId和clientSecret:

  • 访问Google Cloud Console;
  • 从顶部项目下拉菜单创建或选择现有项目;
  • 从侧边栏菜单选择「APIs & Services」>「Credentials」;
  • 在「Credentials」页面点击「Create Credentials」>「OAuth client ID」;
  • 在「Create OAuth client」界面选择「Web application」并点击「Create」;
  • 在后续界面中查看clientId($clientId)和clientSecret($clientSecret)。

现在的问题是,访问授权链接后被重定向到包含授权码的localhost地址,但不知道接下来如何获取$refreshToken,也不确定这段代码是否可用,或者有没有其他可行方法。


解决方案

一、获取Refresh Token的步骤

你已经拿到了授权码,接下来需要用这个授权码换取Refresh Token和Access Token:

  1. 构造POST请求到https://accounts.google.com/o/oauth2/token,请求参数包括:
    • client_id:你的OAuth客户端ID
    • client_secret:你的OAuth客户端密钥
    • code:你从重定向URL中拿到的授权码
    • grant_type:固定为authorization_code
    • redirect_uri:必须和你创建OAuth客户端时填写的重定向URI完全一致(如果当时填的是http://localhost,这里就用这个)
  2. 用PHP发送这个请求(可以用curl实现),响应结果中会包含refresh_token字段,这就是你需要的$refreshToken,请妥善保存这个值(它不会过期,除非手动撤销授权)。

示例代码(用于换取Refresh Token):

function get_refresh_token($clientId, $clientSecret, $authCode, $redirectUri) {
    $url = 'https://accounts.google.com/o/oauth2/token';
    $data = [
        'client_id' => $clientId,
        'client_secret' => $clientSecret,
        'code' => $authCode,
        'grant_type' => 'authorization_code',
        'redirect_uri' => $redirectUri
    ];

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); // 建议开启SSL验证,避免安全风险
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));

    $result = curl_exec($ch);
    curl_close($ch);

    $result = json_decode($result, true);
    if (isset($result['refresh_token'])) {
        return $result['refresh_token'];
    } else {
        // 处理错误,比如输出错误信息
        var_dump($result);
        return null;
    }
}

二、修复现有代码的问题

你拿到的GPT代码存在几个问题,需要调整:

  1. send_notification_common函数中$refreshToken未定义,需要将其作为参数传入,或者从配置中读取
  2. 建议开启CURLOPT_SSL_VERIFYPEER和CURLOPT_SSL_VERIFYHOST的验证,关闭会有安全风险
  3. 消息结构中notification和data使用同一个$message可能不符合需求,建议分开处理(notification是前台显示的通知内容,data是自定义业务数据)

修复后的代码示例:

function get_access_token($clientId, $clientSecret, $refreshToken) {
    $url = 'https://accounts.google.com/o/oauth2/token';
    $data = [
        'client_id' => $clientId,
        'client_secret' => $clientSecret,
        'refresh_token' => $refreshToken,
        'grant_type' => 'refresh_token',
    ];

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));

    $result = curl_exec($ch);
    curl_close($ch);

    $result = json_decode($result, true);

    return isset($result['access_token']) ? $result['access_token'] : null;
}

function send_notification_common($tokens, $notification, $data, $clientId, $clientSecret, $refreshToken) {
    $accessToken = get_access_token($clientId, $clientSecret, $refreshToken);
    if (!$accessToken) {
        return 'Failed to get access token';
    }

    $url = 'https://fcm.googleapis.com/v1/projects/gymgym-4ecef/messages:send';
    $fields = [
        'message' => [
            'token' => $tokens,
            'notification' => $notification,
            'data' => $data
        ]
    ];
    $headers = [
        'Authorization: Bearer ' . $accessToken,
        'Content-Type: application/json'
    ];

    $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_VERIFYHOST, 2);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fields));
    $result = curl_exec($ch);
    if ($result === FALSE) {
        return 'Curl failed: ' . curl_error($ch);
    }
    curl_close($ch);
    return $result;
}

// 使用示例
$clientId = '你的ClientId';
$clientSecret = '你的ClientSecret';
$refreshToken = '你获取到的RefreshToken';
$deviceToken = '目标设备的FCM令牌';
$notification = [
    'title' => '通知标题',
    'body' => '通知内容'
];
$data = [
    'key1' => 'value1',
    'key2' => 'value2'
];

$response = send_notification_common($deviceToken, $notification, $data, $clientId, $clientSecret, $refreshToken);
echo $response;

三、替代方案:使用服务账号密钥

考虑到PHP 5.6已经停止维护,且OAuth流程相对繁琐,你可以选择使用Google服务账号来简化认证:

  1. 在Google Cloud Console的「Credentials」页面,点击「Create Credentials」>「Service account」
  2. 创建服务账号后,生成并下载JSON格式的密钥文件
  3. 使用PHP的Google客户端库(注意选择支持PHP 5.6的版本,比如google/apiclient:^2.12)来获取访问令牌,然后调用FCM API

不过由于PHP 5.6的兼容性限制,客户端库的部分功能可能受限,但服务账号的方式比OAuth授权码流程更适合服务器端场景,不需要手动获取Refresh Token。


内容的提问来源于stack exchange,提问作者RaGyun

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 21:14:58