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

Google API令牌每小时过期后重复要求用户授权问题排查

代码核心错误点

  • 授权回调逻辑顺序完全错误:你在没有刷新令牌的分支里,先执行了header("Location: " . $authUrl)跳转,PHP执行header跳转后当前脚本会直接终止,后面判断$_GET['code']、用授权码换令牌的逻辑永远不会被执行。用户授权后跳回你的页面带了code参数,又会触发一次跳转到授权页,陷入死循环,永远拿不到刷新令牌。
  • 缺少强制授权确认的参数:Google OAuth默认只有用户首次授权应用时才会返回refresh_token,你没有设置$client->setPrompt('consent'),后续就算用户重新授权也拿不到新的refresh_token,导致token过期后没有refresh_token可以用来刷新,只能反复要求用户授权。
  • 没有处理授权码为空的异常:如果$_GET['code']不存在,你给$authCode赋值为null,直接传给fetchAccessTokenWithAuthCode必然报错,也会导致授权流程失败。

修正后的代码

function getClient()
{
    $client = new Google_Client();
    $redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
    // 注意:生产环境不要关闭SSL校验,此处仅本地测试用
    $guzzleClient = new \GuzzleHttp\Client(array( 'curl' => array( CURLOPT_SSL_VERIFYPEER => false, ), ));
    $client->setHttpClient($guzzleClient);
    $sample_passthrough_value = 'someregularstring22';
    $client->setApplicationName('GDrive_Trello');
    $client->setScopes('https://www.googleapis.com/auth/drive');
    $client->setAuthConfig('credentials.json');
    $client->setState($sample_passthrough_value);
    $client->setRedirectUri($redirect_uri);
    $client->setAccessType('offline');
    $client->setIncludeGrantedScopes(true);
    // 新增:强制弹出授权确认页,确保每次授权都返回refresh_token
    $client->setPrompt('consent');

    $tokenPath = 'token.json';
    if (file_exists($tokenPath)) {
        $accessToken = json_decode(file_get_contents($tokenPath), true);
        $client->setAccessToken($accessToken);
    }

    // 先处理授权回调,再判断令牌是否过期
    if (isset($_GET['code'])) {
        $authCode = $_GET['code'];
        $accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
        $client->setAccessToken($accessToken);
        if (array_key_exists('error', $accessToken)) {
            throw new Exception(join(', ', $accessToken));
        }
        // 保存新拿到的令牌(包含refresh_token)
        if (!file_exists(dirname($tokenPath))) {
            mkdir(dirname($tokenPath), 0700, true);
        }
        file_put_contents($tokenPath, json_encode($client->getAccessToken()));
        // 跳回当前页面,清除URL里的code参数,避免刷新页面重复使用授权码
        header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
        exit;
    }

    if ($client->isAccessTokenExpired()) {
        if ($client->getRefreshToken()) {
            // 有刷新令牌直接刷新,不需要用户授权
            $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
            // 刷新后要把新的access_token存到文件
            if (!file_exists(dirname($tokenPath))) {
                mkdir(dirname($tokenPath), 0700, true);
            }
            file_put_contents($tokenPath, json_encode($client->getAccessToken()));
        } else {
            // 没有刷新令牌才跳授权页
            $authUrl = $client->createAuthUrl();
            header("Location: " . $authUrl);
            exit;
        }
    }
    return $client;
}

额外注意事项

  • 首次使用修正后的代码时,需要先删除旧的token.json文件,重新走一次授权流程,拿到带refresh_token的新令牌
  • 生产环境请不要关闭CURLOPT_SSL_VERIFYPEER参数,避免出现安全风险
  • 如果你用的是Google测试状态的OAuth应用,用户授权的refresh_token有效期只有7天,需要把应用发布到正式状态才能拿到长期有效的refresh_token

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 03:48:03