PHP 5.6下FCM迁移至HTTP v1协议及Google OAuth2令牌获取问题
问题背景
我正在将PHP 5.6应用重构为Java应用,预计6月完成。在此期间需要迁移推送通知确保正常发送,但原《Google App Engine PHP 5标准环境文档》(讲解Google OAuth2用户认证)已于1月30日终止,有两个核心问题:
- 如何在PHP 5.6版本中获取Google OAuth2令牌?
- 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:
- 构造POST请求到
https://accounts.google.com/o/oauth2/token,请求参数包括:client_id:你的OAuth客户端IDclient_secret:你的OAuth客户端密钥code:你从重定向URL中拿到的授权码grant_type:固定为authorization_coderedirect_uri:必须和你创建OAuth客户端时填写的重定向URI完全一致(如果当时填的是http://localhost,这里就用这个)
- 用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代码存在几个问题,需要调整:
send_notification_common函数中$refreshToken未定义,需要将其作为参数传入,或者从配置中读取- 建议开启
CURLOPT_SSL_VERIFYPEER和CURLOPT_SSL_VERIFYHOST的验证,关闭会有安全风险 - 消息结构中
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服务账号来简化认证:
- 在Google Cloud Console的「Credentials」页面,点击「Create Credentials」>「Service account」
- 创建服务账号后,生成并下载JSON格式的密钥文件
- 使用PHP的Google客户端库(注意选择支持PHP 5.6的版本,比如
google/apiclient:^2.12)来获取访问令牌,然后调用FCM API
不过由于PHP 5.6的兼容性限制,客户端库的部分功能可能受限,但服务账号的方式比OAuth授权码流程更适合服务器端场景,不需要手动获取Refresh Token。
内容的提问来源于stack exchange,提问作者RaGyun
相关产品推荐
相关产品推荐

