google php客户端库在使用cURL错误6进行身份验证时中断

时间:2017-10-30 18:36:22

标签: google-api google-oauth google-login google-php-sdk

我收到了谷歌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***

我创建了一个网络applicationCredentials

定义了重定向网址, Redirect url

启用G +, G+ service

问题是即使加载了所有类,我的代码也无法通过$client->authenticate($_GET['code']);

我为你准备了一个测试文件:在http://localhost/auth/google/loginhttp://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 并获取具有以下形式的访问令牌:

[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;
}

感谢this优秀的帖子。

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

谢谢。

1 个答案:

答案 0 :(得分:0)

经过大量搜索,

作为google文档says

  

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

同样在OpenId

  

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

所以,我将所有库发送到我的回收站,并使用REST和cURL来发出服务器请求。 让我们定义一个类GoogleToken,其中包含与我们对Google服务器的请求相关的属性,如网址,客户端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. 用户信息请求

  3. 而不是相反,您将遇到来自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] => xxx@gmail.com
           * [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;
       }
    }
    

    现在我们可以致电谷歌并制作一个登录应用程序:

    file 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之上处理我们的端点/请求的图层替换。

    玩得开心!