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

PHP调用Google Calendar API离线获取事件列表遇问题求助

解决方案

1. 解决OAuth提示“应用不安全”的问题

这个提示源于你的应用处于未验证状态,Google会对未验证的外部应用显示安全警告,解决方式分两种场景:

  • 内部/个人使用:登录Google Cloud Console,进入项目的「OAuth同意屏幕」页面,将用户类型设置为「内部」,保存后警告会直接消失。
  • 公开外部使用:需要提交应用验证流程:
    1. 完善应用基本信息(名称、logo、联系邮箱)。
    2. 添加隐私政策、服务条款的HTTPS可访问链接。
    3. 列出应用使用的所有OAuth范围,并提供清晰的使用说明。
    4. 添加测试用户(验证期间仅测试用户可正常授权,无警告),提交审核即可。
  • 测试场景下,可临时点击授权页面的「高级」-「继续访问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授权:

  1. 在Google Cloud Console创建服务账号,下载密钥文件。
  2. 将你的日历共享给服务账号的邮箱地址。
  3. 直接用服务账号密钥初始化客户端,无需OAuth授权流程,即可永久访问日历。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 09:37:04