PHP调用Google Calendar API离线获取事件列表遇问题求助
解决方案
1. 解决OAuth提示“应用不安全”的问题
这个提示源于你的应用处于未验证状态,Google会对未验证的外部应用显示安全警告,解决方式分两种场景:
- 内部/个人使用:登录Google Cloud Console,进入项目的「OAuth同意屏幕」页面,将用户类型设置为「内部」,保存后警告会直接消失。
- 公开外部使用:需要提交应用验证流程:
- 完善应用基本信息(名称、logo、联系邮箱)。
- 添加隐私政策、服务条款的HTTPS可访问链接。
- 列出应用使用的所有OAuth范围,并提供清晰的使用说明。
- 添加测试用户(验证期间仅测试用户可正常授权,无警告),提交审核即可。
- 测试场景下,可临时点击授权页面的「高级」-「继续访问xxx(不安全)」跳过警告,但不建议正式环境使用。
2. 实现无需重复授权的离线访问
当前代码将access_token存在Session中,Session属于浏览器会话级存储,换浏览器或清除缓存就会失效。要实现持久化离线访问,核心是保存并复用refresh_token:
步骤1:修改授权回调逻辑,持久化存储refresh_token
refresh_token仅在第一次授权且设置access_type=offline时返回,需将其存储到数据库或加密文件中(不要存在Session)。修改oauth2callback.php:
require_once __DIR__.'/vendor/autoload.php'; session_start(); $client = new Google\Client(); $client->setAuthConfig('xxx.json'); // 注意:新版本API已废弃setAuthConfigFile,改用setAuthConfig $client->setRedirectUri('https://' . $_SERVER['HTTP_HOST'] . '/xxx/oauth2callback.php'); $client->addScope('https://www.googleapis.com/auth/calendar.events.readonly'); $client->setAccessType("offline"); // 必须设置才能获取refresh_token $client->setPrompt('consent'); // 测试时可强制每次授权返回refresh_token if (! isset($_GET['code'])) { $auth_url = $client->createAuthUrl(); header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL)); } else { $client->authenticate($_GET['code']); $token_data = $client->getAccessToken(); // 示例用文件存储token,实际建议用数据库根据用户ID存储 file_put_contents('token_storage.json', json_encode($token_data)); $_SESSION['access_token'] = $token_data; $redirect_uri = 'https://' . $_SERVER['HTTP_HOST'] . '/xxx/'; header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL)); }
步骤2:修改首页逻辑,自动刷新过期token
在index.php中,从持久化存储读取token,检查是否过期,过期则用refresh_token自动刷新:
require_once __DIR__.'/vendor/autoload.php'; session_start(); $client = new Google\Client(); $client->setAuthConfig('xxx.json'); $client->setAccessType("offline"); $client->addScope('https://www.googleapis.com/auth/calendar.events.readonly'); // 从存储读取token数据(示例用文件,实际根据用户ID从数据库读取) $token_file = 'token_storage.json'; if (file_exists($token_file)) { $token_data = json_decode(file_get_contents($token_file), true); $client->setAccessToken($token_data); // 检查access_token是否过期,自动刷新 if ($client->isAccessTokenExpired()) { $new_token = $client->fetchAccessTokenWithRefreshToken($token_data['refresh_token']); $token_data = array_merge($token_data, $new_token); file_put_contents($token_file, json_encode($token_data)); $client->setAccessToken($token_data); } // 获取并展示日历事件 $calendarId = 'primary'; $optParams = array( 'maxResults' => 10, 'orderBy' => 'startTime', 'singleEvents' => TRUE, 'timeMin' => date('c'), ); $service = new Google_Service_Calendar($client); $results = $service->events->listEvents($calendarId, $optParams); if (count($results->getItems()) == 0) { print "No upcoming events found.<br />"; } else { print "Upcoming events:<br />"; foreach ($results->getItems() as $event) { $start = $event->start->dateTime; if (empty($start)) { $start = $event->start->date; } printf("%s (%s)<br />", $event->getSummary(), $start); } } } else { // 无存储token时跳转授权 $redirect_uri = 'https://' . $_SERVER['HTTP_HOST'] . '/xxx/oauth2callback.php'; header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL)); }
额外方案:服务账号(适合固定访问特定日历场景)
如果只是让特定用户访问你的日历,可使用服务账号替代OAuth授权:
- 在Google Cloud Console创建服务账号,下载密钥文件。
- 将你的日历共享给服务账号的邮箱地址。
- 直接用服务账号密钥初始化客户端,无需OAuth授权流程,即可永久访问日历。
内容的提问来源于stack exchange,提问作者teo2600
相关产品推荐
相关产品推荐

