Google Calendar 403 禁止 PHP 服务器到服务器通信

Posted

技术标签:

【中文标题】Google Calendar 403 禁止 PHP 服务器到服务器通信【英文标题】:Google Calendar 403 Forbidden PHP Server to Server Communication 【发布时间】:2017-06-22 19:58:33 【问题描述】:
  <?php

    include('lead1.php');
    require_once __DIR__ . '/vendor/autoload.php';

    global $link;

        $emailmsgsql =  "SELECT *
                        FROM psleads WHERE agreeid = '6'";
        $msgreqsres = mysqli_query($link, $emailmsgsql); // or die(mysql_error()0);
        $msgreqs = $msgreqsres->fetch_assoc();

        $start = $msgreqs['contractbegindate'] . ' ' . $msgreqs['contractbegintime'];
        $end = $msgreqs['contractenddate'] . ' ' . $msgreqs['contractendtime'];

        $startDT = new DateTime($start, new DateTimeZone('Pacific/Honolulu'));
        $endDT = new DateTime($end, new DateTimeZone('Pacific/Honolulu'));

        $startDTw3c = $startDT->format(DateTime::W3C);
        $endDTw3c = $endDT->format(DateTime::W3C);

        putenv('GOOGLE_APPLICATION_CREDENTIALS=./service-account.json');

        define('CREDENTIALS_PATH', '~/calendar-php.json');
        define('CLIENT_SECRET_PATH', './client_secret.json');
        //define('CLIENT_SECRET_PATH', __DIR__ . '/client_secret.json');

        $client = new Google_Client();
        $client->setApplicationName("Paradise_Sound_Booking_Calendar");
        $client->addScope('https://www.googleapis.com/auth/calendar');
        $client->setAuthConfig(CLIENT_SECRET_PATH);
        $client->setClientId('532085378494-s908fs5mu4rf2e2s60cecgaprg9pem1p.apps.googleusercontent.com');

        $client->setDeveloperKey("XXXXX");//flo.gd

        $client->useApplicationDefaultCredentials();

        // Load previously authorized credentials from a file.
        function expandHomeDirectory($path) 
          $homeDirectory = getenv('HOME');
          if (empty($homeDirectory)) 
            $homeDirectory = getenv('HOMEDRIVE') . getenv('HOMEPATH');
          
          return str_replace('~', realpath($homeDirectory), $path);
        

$credentialsPath = expandHomeDirectory(CREDENTIALS_PATH);
if (file_exists($credentialsPath)) 
    $accessToken = json_decode(file_get_contents($credentialsPath), true);
 else 
    // Request authorization from the user.
    $authUrl = $client->createAuthUrl();
    printf("Open the following link in your browser:\n%s\n", $authUrl);
    print 'Enter verification code: ';
    //$authCode = trim(fgets(STDIN));
    $authCode = 'Manually pasted return code into script here';

    // Exchange authorization code for an access token.
    $accessToken = $client->fetchAccessTokenWithAuthCode($authCode);

    // Store the credentials to disk.
    if(!file_exists(dirname($credentialsPath))) 
      mkdir(dirname($credentialsPath), 0700, true);
    
    file_put_contents($credentialsPath, json_encode($accessToken));
    printf("Credentials saved to %s\n", $credentialsPath);


$client->setAccessToken($accessToken);

// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) 
    $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
    file_put_contents($credentialsPath, json_encode($client->getAccessToken()));




        $service = new Google_Service_Calendar($client);

        $event = new Google_Service_Calendar_Event(array(
          'summary' => 'Booked Event ' . $msgreqs['contractbegindate'],
          'start' => array(
            'dateTime' => $startDTw3c,
            //'dateTime' => '2015-05-28T09:00:00-07:00',
            'timeZone' => 'Pacific/Honolulu',
          ),
          'end' => array(
            'dateTime' => $endDTw3c,
            'timeZone' => 'Pacific/Honolulu',
          )
        ));

        $calendarId = 'iddnpsbinrifod2826eqo1kmoo@group.calendar.google.com';
        $eventres = $service->events->insert($calendarId, $event);

        echo json_encode($eventres);
?>

这是我用来测试将事件插入到我的谷歌日历中的 PHP 代码。

我以为我可以只使用一个 API 密钥,但谷歌似乎有这种令人费解的 OAUTH 方式,我只是想不通。我可以在我的 API Developers Console 中看到我所有的 403 错误。

有没有人有工作代码可以在我的日历中插入简单的事件。

上下文:

我将从 paypal 收到一个 IPN(完成),这将触发这个脚本,该脚本会将一个事件插入我的日历,而不是用户。任何人都可以在不向我介绍谷歌开发者文档的情况下帮助我吗?它们似乎很少,我一遍又一遍地阅读它们,但无济于解决我的问题。

这是我得到的错误:

致命错误:未捕获的异常“Google_Service_Exception”与 消息'“错误”:“错误”:[“域”:“全局”,“原因”: “禁止”,“消息”:“禁止”],“代码”:403,“消息”: “禁止” ' 在 /home/dahfrench/flo.gd/src/Google/Http/REST.php:118 堆栈跟踪:#0 /home/dahfrench/flo.gd/src/Google/Http/REST.php(94): Google_Http_REST::decodeHttpResponse(对象(GuzzleHttp\Psr7\Response), 对象(GuzzleHttp\Psr7\Request),'Google_Service_...')#1 [内部 功能]: Google_Http_REST::doExecute(Object(GuzzleHttp\Client), 对象(GuzzleHttp\Psr7\Request),'Google_Service_...')#2 /home/dahfrench/flo.gd/src/Google/Task/Runner.php(181): call_user_func_array(Array, Array) #3 /home/dahfrench/flo.gd/src/Google/Http/REST.php(58): Google_Task_Runner->run() #4 /home/dahfrench/flo.gd/src/Google/Client.php(789): Google_Http_REST::execute(Object(GuzzleHttp\Client), 对象(GuzzleHttp\Psr7\Request),'Google_Service_...',数组)#5 /home/dahfrench/flo.gd/src/Google/Service/Resource.php(232):粘进去 /home/dahfrench/flo.gd/src/Google/Http/REST.php 在第 118 行

【问题讨论】:

另外,在我做任何 OAuth 废话之前,我遇到了这个错误,因为我认为我需要谷歌建议的服务帐户身份验证来进行服务器到服务器的通信。 API 密钥用于访问公共数据(甚至可能不是 oauth),Oauth2 和服务帐户用于访问私人用户数据。 【参考方案1】:

您可能希望最终确定要实施的身份验证:用户需要登录才能执行/请求 Google 服务或将域范围的权限委派给服务帐户。

如果您将使用 OAuth 2.0:

您的应用程序必须使用 OAuth 2.0 来授权请求​​。

Sample Code from Google:

<?php
require_once __DIR__ . '/vendor/autoload.php';


define('APPLICATION_NAME', 'Google Calendar API PHP Quickstart');
define('CREDENTIALS_PATH', '~/.credentials/calendar-php-quickstart.json');
define('CLIENT_SECRET_PATH', __DIR__ . '/client_secret.json');
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/calendar-php-quickstart.json
define('SCOPES', implode(' ', array(
  Google_Service_Calendar::CALENDAR_READONLY)
));

if (php_sapi_name() != 'cli') 
  throw new Exception('This application must be run on the command line.');


/**
 * Returns an authorized API client.
 * @return Google_Client the authorized client object
 */
function getClient() 
  $client = new Google_Client();
  $client->setApplicationName(APPLICATION_NAME);
  $client->setScopes(SCOPES);
  $client->setAuthConfig(CLIENT_SECRET_PATH);
  $client->setAccessType('offline');

  // Load previously authorized credentials from a file.
  $credentialsPath = expandHomeDirectory(CREDENTIALS_PATH);
  if (file_exists($credentialsPath)) 
    $accessToken = json_decode(file_get_contents($credentialsPath), true);
   else 
    // Request authorization from the user.
    $authUrl = $client->createAuthUrl();
    printf("Open the following link in your browser:\n%s\n", $authUrl);
    print 'Enter verification code: ';
    $authCode = trim(fgets(STDIN));

    // Exchange authorization code for an access token.
    $accessToken = $client->fetchAccessTokenWithAuthCode($authCode);

    // Store the credentials to disk.
    if(!file_exists(dirname($credentialsPath))) 
      mkdir(dirname($credentialsPath), 0700, true);
    
    file_put_contents($credentialsPath, json_encode($accessToken));
    printf("Credentials saved to %s\n", $credentialsPath);
  
  $client->setAccessToken($accessToken);

  // Refresh the token if it's expired.
  if ($client->isAccessTokenExpired()) 
    $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
    file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
  
  return $client;


/**
 * Expands the home directory alias '~' to the full path.
 * @param string $path the path to expand.
 * @return string the expanded path.
 */
function expandHomeDirectory($path) 
  $homeDirectory = getenv('HOME');
  if (empty($homeDirectory)) 
    $homeDirectory = getenv('HOMEDRIVE') . getenv('HOMEPATH');
  
  return str_replace('~', realpath($homeDirectory), $path);


// Get the API client and construct the service object.
$client = getClient();
$service = new Google_Service_Calendar($client);

// Print the next 10 events on the user's calendar.
$calendarId = 'primary';
$optParams = array(
  'maxResults' => 10,
  'orderBy' => 'startTime',
  'singleEvents' => TRUE,
  'timeMin' => date('c'),
);
$results = $service->events->listEvents($calendarId, $optParams);

if (count($results->getItems()) == 0) 
  print "No upcoming events found.\n";
 else 
  print "Upcoming events:\n";
  foreach ($results->getItems() as $event) 
    $start = $event->start->dateTime;
    if (empty($start)) 
      $start = $event->start->date;
    
    printf("%s (%s)\n", $event->getSummary(), $start);
  

如果您将使用 Google Apps 域范围授权:

授权服务帐户代表域中的用户访问数据有时被称为“将域范围的权限委派给服务帐户”。

来自SO post 的示例代码:

function calendarize ($title, $desc, $ev_date, $cal_id) 

    session_start();

    /************************************************
    Make an API request authenticated with a service
    account.
    ************************************************/
    set_include_path( '../google-api-php-client/src/');

    require_once 'Google/Client.php';
    require_once 'Google/Service/Calendar.php';

    //obviously, insert your own credentials from the service account in the Google Developer's console
    $client_id = '843319906820-xxxxxxxxxxxxxxxxxxxdcqal54p1he6.apps.googleusercontent.com';
    $service_account_name = '843319906820-xxxxxxxxxxxxxxxxxxxdcqal54p1he6@developer.gserviceaccount.com';
    $key_file_location = '../google-api-php-client/calendar-xxxxxxxxxxxx.p12';

    if (!strlen($service_account_name) || !strlen($key_file_location))
        echo missingServiceAccountDetailsWarning();

    $client = new Google_Client();
    $client->setApplicationName("Whatever the name of your app is");

    if (isset($_SESSION['service_token'])) 
        $client->setAccessToken($_SESSION['service_token']);
    

    $key = file_get_contents($key_file_location);
    $cred = new Google_Auth_AssertionCredentials(
        $service_account_name, 
        array('https://www.googleapis.com/auth/calendar'), 
        $key
    );
    $client->setAssertionCredentials($cred);
    if($client->getAuth()->isAccessTokenExpired()) 
        $client->getAuth()->refreshTokenWithAssertion($cred);
    
    $_SESSION['service_token'] = $client->getAccessToken();

    $calendarService = new Google_Service_Calendar($client);
    $calendarList = $calendarService->calendarList;

    //Set the Event data
    $event = new Google_Service_Calendar_Event();
    $event->setSummary($title);
    $event->setDescription($desc);

    $start = new Google_Service_Calendar_EventDateTime();
    $start->setDateTime($ev_date);
    $event->setStart($start);

    $end = new Google_Service_Calendar_EventDateTime();
    $end->setDateTime($ev_date);
    $event->setEnd($end);

    $createdEvent = $calendarService->events->insert($cal_id, $event);

    echo $createdEvent->getId();
 

?>

注意:如果您打算只使用一个日历,我建议您使用服务帐户,然后将您的日历共享到该帐户以避免 403 : Forbidden 如相关SO post 所述

希望这会有所帮助。

【讨论】:

是的,在我发布这个之后,我意识到我的代码是多么混乱。我相信我想要一个服务帐户,因为用户永远不必进行 oauth 登录。我将尝试根据我的信誉定制您的示例,然后回复以接受您对代码工作的回答。谢谢! 别忘了将日历分享到服务帐号 你的意思是在谷歌开发者控制台中为我的项目使用日历 api 吗?我想我做到了,但我不确定。 查看***.com/a/11897793/5995040 以了解如何将指定的帐户获取到 Google Api 控制台并将日历共享给它

以上是关于Google Calendar 403 禁止 PHP 服务器到服务器通信的主要内容,如果未能解决你的问题,请参考以下文章

403(禁止),Google Speech API 上的无效键错误

Google Roads API - HTTP 错误 403:禁止

即使似乎授予访问权限,Google Calendar API在OAuth期间也会出现403错误

HTTPError:HTTP 错误 403:在 Google Colab 上被禁止

Google Calendar API - 禁止 - 服务帐户错误

Google Docs:无法在 C# 中使用管理访问/模拟(禁止 403)导出/下载用户的文档