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
相关产品推荐
相关产品推荐

