google php 客户端库因 cURL 错误 6 导致身份验证中断

问题描述 投票:0回答:1

我有谷歌api-php-client,但我无法登录,因为我总是收到错误:

[02-Nov-2017 15:30:52 Europe/Helsinki] ***START***
scripto\CustomException: [0: E_CUSTOM: Suppressed or Custom error] cURL error 6: Could not resolve host: www.googleapis.com (see http://curl.haxx.se/libcurl/c/libcurl-errors.html).
in /home/suexec-test/server/GuzzleHttp/Handler/CurlFactory.php on line 186
[Trace]:
#0 /home/suexec-test/server/ExceptionFactory.php(117): scripto\CustomException::buildFromException(Object(GuzzleHttp\Exception\ConnectException))
#1 [internal function]: scripto\ExceptionFactory::exception_handler(Object(GuzzleHttp\Exception\ConnectException))
#2 {main}
[Previous]:
scripto\CustomException: [0: E_CUSTOM: Suppressed or Custom error] cURL error 6: Could not resolve host: www.googleapis.com (see http://curl.haxx.se/libcurl/c/libcurl-errors.html).
in /home/suexec-test/server/GuzzleHttp/Handler/CurlFactory.php on line 186
[Trace]:
#0 /home/suexec-test/server/GuzzleHttp/Handler/CurlFactory.php(150): GuzzleHttp\Handler\CurlFactory::createRejection(Object(GuzzleHttp\Handler\EasyHandle), Array)
#1 /home/suexec-test/server/GuzzleHttp/Handler/CurlFactory.php(103): GuzzleHttp\Handler\CurlFactory::finishError(Object(GuzzleHttp\Handler\CurlHandler), Object(GuzzleHttp\Handler\EasyHandle), Object(GuzzleHttp\Handler\CurlFactory))
#2 /home/suexec-test/server/GuzzleHttp/Handler/CurlHandler.php(43): GuzzleHttp\Handler\CurlFactory::finish(Object(GuzzleHttp\Handler\CurlHandler), Object(GuzzleHttp\Handler\EasyHandle), Object(GuzzleHttp\Handler\CurlFactory))
#3 /home/suexec-test/server/GuzzleHttp/Handler/Proxy.php(28): GuzzleHttp\Handler\CurlHandler->__invoke(Object(GuzzleHttp\Psr7\Request), Array)
#4 /home/suexec-test/server/GuzzleHttp/Handler/Proxy.php(51): GuzzleHttp\Handler\Proxy::GuzzleHttp\Handler\{closure}(Object(GuzzleHttp\Psr7\Request), Array)
#5 /home/suexec-test/server/GuzzleHttp/PrepareBodyMiddleware.php(66): GuzzleHttp\Handler\Proxy::GuzzleHttp\Handler\{closure}(Object(GuzzleHttp\Psr7\Request), Array)
#6 /home/suexec-test/server/GuzzleHttp/Middleware.php(30): GuzzleHttp\PrepareBodyMiddleware->__invoke(Object(GuzzleHttp\Psr7\Request), Array)
#7 /home/suexec-test/server/GuzzleHttp/RedirectMiddleware.php(70): GuzzleHttp\Middleware::GuzzleHttp\{closure}(Object(GuzzleHttp\Psr7\Request), Array)
#8 /home/suexec-test/server/GuzzleHttp/Middleware.php(57): GuzzleHttp\RedirectMiddleware->__invoke(Object(GuzzleHttp\Psr7\Request), Array)
#9 /home/suexec-test/server/GuzzleHttp/HandlerStack.php(67): GuzzleHttp\Middleware::GuzzleHttp\{closure}(Object(GuzzleHttp\Psr7\Request), Array)
#10 /home/suexec-test/server/GuzzleHttp/Client.php(281): GuzzleHttp\HandlerStack->__invoke(Object(GuzzleHttp\Psr7\Request), Array)
#11 /home/suexec-test/server/GuzzleHttp/Client.php(103): GuzzleHttp\Client->transfer(Object(GuzzleHttp\Psr7\Request), Array)
#12 /home/suexec-test/server/GuzzleHttp/Client.php(110): GuzzleHttp\Client->sendAsync(Object(GuzzleHttp\Psr7\Request), Array)
#13 /home/suexec-test/server/Google/Auth/HttpHandler/Guzzle6HttpHandler.php(34): GuzzleHttp\Client->send(Object(GuzzleHttp\Psr7\Request), Array)
#14 /home/suexec-test/server/Google/Auth/OAuth2.php(501): Google\Auth\HttpHandler\Guzzle6HttpHandler->__invoke(Object(GuzzleHttp\Psr7\Request))
#15 /home/suexec-test/server/Google/Google_Client.php(195): Google\Auth\OAuth2->fetchAuthToken(Object(Google\Auth\HttpHandler\Guzzle6HttpHandler))
#16 /home/suexec-test/server/Google/Google_Client.php(174): Google_Client->fetchAccessTokenWithAuthCode('4/4BhYCtvi43DGf...')
#17 /home/suexec-test/tests/testGoogle-4.php(56): Google_Client->authenticate('4/4BhYCtvi43DGf...')
#18 /home/suexec-test/server/Dispatcher.php(96): require_once('/home/suexec-te...')
#19 /home/suexec-test/server/Controller.php(68): scripto\Dispatcher->run()
#20 /home/suexec-test/public_html/index.php(7): scripto\Controller->run()
#21 {main}
***END***

我创建了一个网络应用程序Credentials

定义了重定向网址, Redirect url

并启用G+, G+ service

问题是,即使加载了所有类,我的代码也无法通过

$client->authenticate($_GET['code']);

我为您准备了一个测试文件:在

http://localhost/auth/google/login
http://localhost/auth/google/logout
您应该调用同一个文件来观察抛出的错误。

测试文件是对this帖子的修改:

<?php
if (\session_status() != PHP_SESSION_ACTIVE) {
   Session_start();
}

$whoami = $_SERVER['REQUEST_URI'];
$login = $logout = false;
if (\strpos($whoami, '/login') !== false)
   $login = true;
if (\strpos($whoami, '/logout') !== false)
   $logout = true;

/*
 * Configuration and setup Google SDK
 */
$appId              = '519650546062-XXXXXX.apps.googleusercontent.com';
$appSecret          = 'XXXXXXX';
// CALLED BY US & GOOGLE!
$loginUri           = 'http://localhost/auth/google/login';
// CALLED BY US!
$logoutUri          = 'http://localhost/auth/google/logout';
$access             = 'online';
$scopes             = "openid profile email";
$incremental_scopes = false;  // what if for true?
//$state = 123;

//Create Google Client
$client = new Google_Client();
$client->setApplicationName("PHP Google OAuth Login Example");
$client->setClientId($appId);
$client->setClientSecret($appSecret);
$client->setRedirectUri($loginUri);
$client->setAccessType($access);
$client->setIncludeGrantedScopes($incremental_scopes);
$client->addScope($scopes);
//$client->setState($state);

//Send Client Request?
$objOAuthService = new Google_Service_Oauth2($client);

//CALLED BY US
if ($logout) {
  unset($_SESSION['access_token']);
  $client->revokeToken();
  // Back to login!
  header('Location: ' . filter_var($loginUri, FILTER_SANITIZE_URL));
}

//CALLED BY GOOGLE
if (isset($_GET['code'])) {
error_log('1....I\'M GOING TO BREAK...');
   $client->authenticate($_GET['code']);
   $_SESSION['access_token'] = $client->getAccessToken();
   // Back to this file!
   header('Location: ' . filter_var($loginUri, FILTER_SANITIZE_URL));
}

//CALLED BY US 2ND TIME (after previous header)
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
error_log('2....session access token='.$_SESSION['access_token']);
   $client->setAccessToken($_SESSION['access_token']);
}

//ONLY DURING 2ND CALL (from previous if...)
if ($client->getAccessToken()) {
   $userData = $objOAuthService->userinfo->get();
   if(!empty($userData)) {
      // do sth with data!
   }
   $_SESSION['access_token'] = $client->getAccessToken();
error_log('3.....user data:'.print_r($userData, true));
} else {
   $authUrl = $client->createAuthUrl();
}

   $out = <<<EOT
<html>
   <head> 
      <title>Google OAuth v.2.0 Login test</title>
   </head>
   <body>

EOT;
if (isset($authUrl)) {
   $d = \urldecode($authUrl);
   $out .= <<<EOT
   <p>decoded authUrl = '{$d}'</p>
EOT;
   }
$out .= <<<EOT
   <h2>PHP Google OAuth 2.0 Login</h2>
EOT;
if (isset($authUrl)) {
   $out .= <<<EOT
      <p><a href='{$authUrl}'>Login with Google API</a></p>
EOT;
} else {
   $out .= <<<EOT
      <p>Welcome <a href="{$userData['link']}">{$userData['name']}</a>.</p>
      <p>Your email: {$userData['email']}</p>
      <p><a href={$logoutUri}>Logout</a></p>

EOT;
}
$e = \nl2br(\htmlspecialchars(\print_r($_SESSION, true)));
$out .= <<<EOT
      <p><h3>SESSION:</h3></p>
      <p>{$e}</p>
   </body>
</html>
EOT;

echo $out;

代码在

error_log('1....I\'M GOING TO BREAK...');
之后就中断了。 有趣的是,我设法用 curl 绕过了 whole 库并获得了以下形式的访问令牌:

[02-Nov-2017 14:06:38 Europe/Helsinki] Array
(
    [access_token] => ya29.Glv3B...xlSKIL_N67PE4...PYGDjZo-jD8v...ITFTnU
    [expires_in] => 2017-11-02 15:06:37
    [id_token] => eyJhbGciOiJImt...tlf_-Y1hXoCVSp...Ve_bK8F-jTCt...zg
    [token_type] => Bearer
)

但是我找不到端点来获取用户的数据:像这样的东西[偶尔]会中断[我统计了多达90%的失败]

public function userinfo() {
   $access_token = $this->access['access_token'];
   $url = 'https://www.googleapis.com/plus/v1/people/me';
   $ch = curl_init();      
   curl_setopt($ch, CURLOPT_URL, $url);      
   curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
   curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
   curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Bearer '. $access_token));
   $data = json_decode(curl_exec($ch), true);
   $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);      
   if ($http_code != 200)
      throw new \ErrorException(__CLASS__ . "::userinfo() is called but failed to get user information!", 0, 1);
   $this->data = $data;
}

感谢这个优秀的帖子。

知道官方 php 库有什么问题吗? 我在控制台中缺少什么吗?

谢谢你。

php google-api google-oauth google-signin google-api-php-client
1个回答
0
投票

经过一番查找,

如谷歌文档所说

无需安装任何库即可直接调用 OAuth 2.0 端点。 enter image description here

也可在OpenId

本文档描述了我们的 OAuth 2.0 实现 身份验证,符合 OpenID Connect 规范, 并获得 OpenID 认证。使用 OAuth 2.0 中找到的文档 访问 Google API 也适用于此服务。

因此,我将所有库发送到我的回收站,并使用 REST 和 cURL 来发出服务器请求。 让我们定义一个类

GoogleToken
,其中包含与我们对 Google 服务器的请求相关的属性,例如 url、客户端 ID、客户端密钥等:

文件 GoogleToken.php

<?php
namespace test;

/**
 * Google authentication Config class
 */
class GoogleToken {
   /** See the list: https://developers.google.com/identity/protocols/OAuth2WebServer#creatingclient */
   /** the application/client id */
   const appId = '';
   /** the application/client secret */
   const appSecret = '';
   /** the application/client redirect uri after login */
   const loginUri = 'http://localhost/auth/google/login';
   /** the application/client redirect uri after logout */
   const logoutUri = 'http://localhost/auth/google/logout';
   /** use 'online' to get the access token!
    * use 'offline' to get a refresh token along with the access token, 
    * see also 'prompt'!
    */
   const access = 'offline';
   /** the application/client approval prompt [auto|force]
    * use 'force' to get  a refresh token!
    */
   const prompt = 'force';
   /** the application/client access permissions
    *  [openid profile email|see https://developers.google.com/identity/protocols/googlescopes]
    */
   const scopes = 'openid profile email';

   public function __construct() {}
}

上面的类应该被视为一个编程令牌,帮助其他类实现他们的目标(也许这是一个新的范例,你可以评估它)。实际上,它是一个更复杂的类,可以通过数据 IO 方法来满足我们的目的,方便用户管理而无需接触 php 文件等。

让我们构建一个类,向 Google 服务器发出请求并存储(临时)响应,即 Google 发送的实际令牌:

文件 GoogleHttpClient.php

该类的骨架:

public function authenticate($code)
public function verify($access_token)
public function validate($verify_token, $user)
public function refresh($refresh_token)
public function revokeToken($access_token)
public function userInfo($access_token)
public function createAuthUrl()

所有方法都接受参数,因为重定向时内部存储的内容会丢失,这就是我们需要存储机制来提供这些参数的原因。 文件

test.php
提供了存储类属性并在需要时调用它们的顶层。 有趣的是方法
authenticate()
应该按顺序进行:

  1. 验证请求

  2. 用户信息请求

而不是相反,否则您将遇到来自 Google 的延迟/错误。 班级

GoogleHttpClient
:

<?php
namespace test;

/**
 * The GoogleHttpClient is a helper class.
 * 
 * As this class is called between different calls, it needs access to a state
 * storage mechanism like sessions.
 * Even though the methods try to abstract away the complexity of the calls by 
 * a logical organisation of the actions involved, you are responsible for the 
 * order or context under which they are called.
 */
class GoogleHttpClient {
   /** @var GoogleToken $token The GoogleToken class object */
   protected $token;

   /** 
    * @var string[] $auth The authorization data returned by Google in the form:
    * Array(
    *    [access_token] => xxx
    *    [expires_in] => [unix timestamp]
    *    [id_token] => xxx.xxx.xxx
    *    [refresh_token] => xxx
    *    [token_type] => Bearer
    * )
    * 
    * This property should be stored in session (per user) in case we need a 
    * verification or refresh.
    */
   protected $auth;

   /** 
    * @var string[] $verify The response data returned by Google in the form:
    * Array(
    *    [issued_to] => xxx.apps.googleusercontent.com (@see GoogleToken['values']['appId'])
    *    [audience] => xxx.apps.googleusercontent.com (@see GoogleToken['values']['appId'])
    *    [user_id] => xxx
    *    [scope] => xxx (@see createAuthUrl())
    *    [expires_in] => xxx (<= 3600)
    *    [email] => xxx
    *    [verified_email] => 1
    *    [access_type] => xxx (@see createAuthUrl())
    * )
    * 
    * You shouldn't store this property in session.
    */
   protected $verify;

   /** 
    * @var string[] $user The user data returned by Google.
    * 
    * You should verify this property with your native authorization system and
    * integrate with it finally.
    */
   protected $user;

   /** @var string[] $error The error returned by Google */
   protected $error;

   /** 
    * @var boolean $log If you should log errors and assigned properties for debuging
    */
   protected $log;

   const INVALID_TOKEN = 1;

   /**
    * Constructor.
    * 
    * preconditions: a GoogleToken should be passed.
    *
    * postconditions: 
    * 
    * @param GoogleToken $token The GoogleToken that contains initialization values
    * @param boolean $log True, if we want to log errors and assigned properties
    * 
    * @return GoogleHttpClient A GoogleHttpClient object
    */
   public function __construct(GoogleToken $token, $log = false) {
      $this->token = $token;
      $this->log = $log;
   }

   /**
    * It makes a authentication request to Google.
    * 
    * preconditions: after the call to Google at this link, @see createAuthUrl(), 
    *    Google responds by making a call back with a unique id that should be
    *    passed as argument.
    *
    * postconditions: it sets properties $auth, $user, $verify and $error. 
    *    On error, $auth, $verify and $error contain the responses returned  by 
    *    Google depending at the method where error appeared, @see verify(), @see validate().
    *    If the authentication succeeds then, $error is null.
    * 
    * @param string $code A unique request id issued by Google
    * 
    * @return boolean True on success
    */
   public function authenticate($code) {
      $client_id = $this->token::appId;
      $client_secret  = $this->token::appSecret;
      $redirect_uri = $this->token::loginUri;
      $url = 'https://accounts.google.com/o/oauth2/token';
      $curlPost = 'client_id=' . $client_id . '&client_secret=' . $client_secret . '&redirect_uri=' . $redirect_uri . '&code='. $code . '&grant_type=authorization_code';
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $url);      
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);    
      curl_setopt($ch, CURLOPT_POST, 1);     
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
      curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost);   
      $data = json_decode(curl_exec($ch), true);
      $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);    
      if ($http_code != 200) {
         if ($this->log)
            error_log(__CLASS__ . '::authenticate() error: http code not 200. Responded: '.print_r($data, true));
         $this->error = $data;
      } else {
         if ($this->log)
            error_log(__CLASS__ . '::auth='.print_r($data, true));
         $this->verify($data['access_token']);
         $this->user = $this->userInfo($data['access_token']);
         $this->validate($this->verify, $this->user);
      }
      $this->auth = $data;
      if ($this->error)
         return false;
      else
         return true;
   }

   /**
    * It requests for a verification token from Google.
    * 
    * preconditions: it is called from @see authenticate().
    *    Calling this method from redirects means all properties are null and we 
    *    pass session data.
    *
    * postconditions: it sets property $verify. 
    *    On error, $verify and $error contain the responses returned  by Google.
    *    If the request succeeds then, $error is null.
    * 
    * @param string $access_token Google's authorization response under key 'access_token'
    * 
    * @return boolean True on success
    */
   public function verify($access_token) {
      $client_id = $this->token::appId;
      $client_secret  = $this->token::appSecret; 
      $url = 'https://www.googleapis.com/oauth2/v2/tokeninfo';
      $curlPost = 'access_token='. $access_token;
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $url);      
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);    
      curl_setopt($ch, CURLOPT_POST, 1);     
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
      curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost);   
      $data = json_decode(curl_exec($ch), true);
      $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);    
      if ($http_code != 200) {
         if ($this->log)
            error_log(__CLASS__ . '::verify() error: http code not 200. Responded: '.print_r($data, true));
         $this->error = $data;
      } else {
         if ($this->log)
            error_log(__CLASS__ . '::verify='.print_r($data, true));
      }
      $this->verify = $data;
      if ($this->error)
         return false;
      else
         return true;
   }

   /**
    * It compares properties $verify with $user and GoogleToken.
    * 
    * preconditions: it is called from @see authenticate().
    *    Calling this method from redirects means all properties are null and we 
    *    pass session data.
    *
    * postconditions: on failed validation, $error contains a custom error with fields:
    *    -error_id: a number
    *    -error_description: a message.
    *    If the verification succeeds then, $error is null.
    * 
    * @param string[] $verify_token Google's verification response token
    * @param string $user Google's user info response
    * 
    * @return boolean True on success
    */
   public function validate($verify_token, $user) {
      if (($verify_token['user_id'] != $user['id']) || ($verify_token['email'] != $user['email']) || ($verify_token['issued_to'] != $this->token::appId)) {
         $this->error = array('error_id' => self::INVALID_TOKEN, 'error_description' => 'Access token does not pass validation!');
         if ($this->log)
            error_log(__CLASS__ . '::error='.print_r($this->error, true));
      }
      if ($this->error)
         return false;
      else
         return true;
   }

   /**
    * It is called when property $auth has expired.
    * 
    * preconditions: authentication has been run, @see authenticate().
    *    Calling this method from redirects means all properties are null and we 
    *    pass session data.
    *
    * postconditions: it re-sets property $auth. 
    *    On error, $auth and $error contain the responses returned  by Google.
    *    If the refresh succeeds then, $error is null.
    * 
    * @param string $refresh_token Google's authorization response under key 'refresh_token'
    * 
    * @return boolean True on success
    */
   public function refresh($refresh_token) {
      $client_id = $this->token::appId;
      $client_secret  = $this->token::appSecret; 
      $url = 'https://accounts.google.com/o/oauth2/token';
      $curlPost = 'client_id=' . $client_id . '&client_secret=' . $client_secret . '&refresh_token='. $refresh_token . '&grant_type=refresh_token';
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $url);      
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);    
      curl_setopt($ch, CURLOPT_POST, 1);     
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
      curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost);   
      $data = json_decode(curl_exec($ch), true);
      $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);    
      if ($http_code != 200) {
         if ($this->log)
            error_log(__CLASS__ . '::refresh() error: http code not 200. Responded: '.print_r($data, true));
         $this->error = $data;
      } else {
         if ($this->log)
            error_log(__CLASS__ . '::auth='.print_r($data, true));
      }
      $this->auth = $data;
      if ($this->error)
         return false;
      else
         return true;
   }

   /**
    * It un-registers application from user's account but id does not (and cannot)
    * logout user from Google.
    * 
    * preconditions: authentication has been run, @see authenticate().
    *    Calling this method from redirects means all properties are null and we 
    *    pass session data.
    *
    * postconditions: it un-registers application from user's account. 
    *    On error, $error contains the response returned  by Google.
    *    If the revoke succeeds then, all properties are nullified.
    * 
    * @param string $access_token Google's authorization response under key 'access_token'
    * 
    * @return boolean True on success
    */
   public function revokeToken($access_token) {
      $client_id = $this->token::appId;
      $client_secret  = $this->token::appSecret; 
      $url = 'https://accounts.google.com/o/oauth2/revoke';
      $curlPost = 'token=' . $access_token;
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $url);      
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);    
      curl_setopt($ch, CURLOPT_POST, 1);     
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
      curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost);   
      $data = json_decode(curl_exec($ch), true);
      $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);    
      if ($http_code != 200) {
         if ($this->log)
            error_log(__CLASS__ . '::revokeToken() error: http code not 200. Responded: '.print_r($data, true));
         $this->error = $data;
      } else {
         $this->auth = null;
         $this->user = null;
         $this->verify = null;
         $this->error = null;
         if ($this->log)
            error_log(__CLASS__ . '::revokeToken() run and erased all properties!');
      }
      if ($this->error)
         return false;
      else
         return true;
   }

   /**
    * It is called from @see authenticate() or you can call it independently at 
    * a later time.
    * 
    * preconditions: authentication has been run, @see authenticate().
    *    Calling this method from redirects means all properties are null and we 
    *    pass session data.
    *
    * postconditions: it sets property $user. 
    *    On error, $user and $error contain the responses returned  by Google.
    *    If the request succeeds then, $error is null.
    * 
    * @param string $access_token Google's authorization response under key 'access_token'
    * 
    * @return string[] A hash array of user's data
    */
   public function userInfo($access_token) {
      /** format of data:
       * Array(
       * [id] => xxx
       * [email] => [email protected]
       * [verified_email] => 1
       * [name] => xxx xxx
       * [given_name] => xxx
       * [family_name] => xxx
       * [picture] => https://lh6.googleusercontent.com/.../photo.jpg?sz=50
       * [locale] => en
       * )
       */
      $url = 'https://www.googleapis.com/userinfo/v2/me';
      $ch = curl_init();      
      curl_setopt($ch, CURLOPT_URL, $url);      
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
      curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Bearer '. $access_token));
      $data = json_decode(curl_exec($ch), true);
      $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);      
      if ($http_code != 200) {
         if ($this->log)
            error_log(__CLASS__ . '::userInfo() error: http code not 200. Responded: '.print_r($data, true));
         $this->error = $data;
      } else {
         if ($this->log)
            error_log(__CLASS__ . '::user='.print_r($data, true));
      }
      $this->user = $data;
      return $this->user;
   }

   /**
    * Creates a authentication link to Google servers.
    */
   public function createAuthUrl() {
      $scopes = \urlencode($this->token::scopes);
      $redirect = \urlencode($this->token::loginUri);
      $appId = $this->token::appId;
      $access = $this->token::access;
      $prompt = $this->token::prompt;
      return "https://accounts.google.com/o/oauth2/auth?scope={$scopes}&redirect_uri={$redirect}&response_type=code&client_id={$appId}&access_type={$access}&approval_prompt={$prompt}";
   }

   /**
    * Returns property $token
    */
   public function getToken() {
      return $this->token;
   }

   /**
    * Returns property $auth
    */
   public function getAuthToken() {
      return $this->auth;
   }

   /**
    * Returns property $verify
    */
   public function getVerifyToken() {
      return $this->verify;
   }

   /**
    * Returns property $user
    */
   public function getUser() {
      return $this->user;
   }

   /**
    * Returns property $error
    */
   public function getError() {
      return $this->error;
   }
}

现在我们可以调用 Google 并制作一个登录应用程序:

文件 test.php

<?php
namespace test;

require_once __DIR__ . '/GoogleToken.php';
require_once __DIR__ . '/GoogleHttpClient.php';

if (\session_status() != PHP_SESSION_ACTIVE) {
   session_start();
}

$whoami = $_SERVER['REQUEST_URI'];
$login = $logout = false;
if (\strpos($whoami, '/login') !== false)
   $login = true;
if (\strpos($whoami, '/logout') !== false)
   $logout = true;

$token = new GoogleToken();
$client = new GoogleHttpClient($token, true);   // enable log
$loginUri = GoogleToken::loginUri;
$logoutUri = GoogleToken::logoutUri;

/* emulates...
 * LOGIN endpoint
 * --------------
 */
if ($login) {
  if (isset($_GET['code'])) {
      if ($client->authenticate($_GET['code']) {
         $_SESSION['google']['auth'] = $auth;
         $_SESSION['google']['auth']['expires_in'] = \date("Y-m-d H:i:s", \time() + $_SESSION['google']['auth']['expires_in']);
         $_SESSION['google']['user'] = $user;
      }
   }
   header('Location: ' . filter_var('http://localhost', FILTER_SANITIZE_URL));
}

/* emulates...
 * LOGOUT endpoint
 * ---------------
 */
if ($logout) {
  $client->revokeToken($_SESSION['google']['auth']['access_token']);
  unset($_SESSION['google']);
  header('Location: ' . filter_var('http://localhost', FILTER_SANITIZE_URL));
}

$userData = '';

/* emulates...
 * Redirect from:
 * --------------
 * - LOGIN endpoint
 */
if (isset($_SESSION['google'])) {
   if(\time() > \strtotime($_SESSION['google']['auth']['expires_in'])) {
      if(!$client->refresh($_SESSION['google']['auth']['access_token'])) {
         $client->revokeToken();
         unset($_SESSION['google']);
         header('Location: ' . filter_var('http://localhost', FILTER_SANITIZE_URL));
      }
   }
   $userData = $_SESSION['google']['user'];

/* emulates...
 * INITIAL endpoint
 * ----------------
 * or,
 * Redirect from:
 * --------------
 * - LOGOUT endpoint or,
 * - failed REFRESH
 */
} else {
   $authUrl = $client->createAuthUrl();
}

$out0 = <<<EOT
<html>
   <head> 
      <title>Google REST OAuth v.2.0 Login test</title>
   </head>
   <body>
EOT;
if (isset($authUrl)) {
   $d = \urldecode($authUrl);
   $out0 .= <<<EOT
      <p>decoded authUrl = '{$d}'</p>
EOT;
}
$out0 .= <<<EOT
      <h2>PHP Google OAuth 2.0 Login</h2>
EOT;
if (isset($authUrl)) {
   $out0 .= <<<EOT
      <p><a href="{$authUrl}">Login with Google API</a></p>
EOT;
} else {
    $out0 .= <<<EOT
      <p>Welcome <img src="{$userData['picture']}" style="width:50px;height:50px;"></img> {$userData['name']}.</p>
      <p>Your email: {$userData['email']}</p>
      <p><a href={$logoutUri}>Logout</a></p>
EOT;
}
$e = \nl2br(\htmlspecialchars(\print_r($_SESSION, true)));
$out0 .= <<<EOT
      <p><h3>SESSION:</h3></p>
      <p>{$e}</p>
   </body>
</html>
EOT;
echo $out0;

正如我们所见,类

GoogleHttpClient
完成了所有工作,但文件
test.php
实际上应该被替换为在
GoogleHttpClient
之上处理端点/请求的另一层。

玩得开心!

© www.soinside.com 2019 - 2024. All rights reserved.