我需要修复一个PHP Google Auth问题。在收到一个400错误请求的代码失败后,我正在尝试交换Auth令牌。
在我的一生中,在阅读文档并一遍又一遍地研究代码后,我无法理解它。
我的网址有问题吗?
$url = 'https://accounts.google.com/o/oauth2/token?&grant_type=authorization_code&code='.$_GET['code']."&client_id=".G_CLIENT_ID."&client_secret=".G_CLIENT_SECRET."&redirect_uri=".REDIRECT_URI;
$context = stream_context_create(
array( 'http' =>
array('method' => 'POST',
)
)
);
echo "<Br> url to fetch : ". $url;
echo "<Br> context to fetch : ". $context;
$response = file_get_contents($url, false, $context);
echo "<Br> fetch response : ". $response;
代码重用是否迫使Google拒绝我的身份验证令牌检索尝试?
谷歌的400错误不会返回太多信息——他们肯定应该提供更多信息吗?
编辑1
print_r返回的请求标头(apache_Request_headers())-->
Array
(
[Accept] => text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
[Accept-Encoding] => gzip,deflate,sdch
[Accept-Language] => en-US,en;q=0.8
[Connection] => keep-alive
[Cookie] => PHPSESSID=ec0b5ff920282245f7ce6d194ba36bd1; _ga=GA1.2.1973782149.1384923620
[Host] => lxxxxxxr.com
[User-Agent] => Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36
)
print_r返回的响应标头(apache_Response_headers())-->
Array
(
[X-Powered-By] => PHP/5.4.20
[Expires] => Thu, 19 Nov 1981 08:52:00 GMT
[Cache-Control] => no-store, no-cache, must-revalidate, post-check=0, pre-check=0
[Pragma] => no-cache
[Content-type] => text/html
)
响应正文-->
<!DOCTYPE html>
<html lang=en>
<meta charset=utf-8>
<meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
<title>Error 400 (Bad Request)!!1</title>
<style> ***some style info*** </style>
<a href=//www.google.com/><img src=//www.google.com/images/errors/logo_sm.gif alt=Google></a>
<p><b>400.</b> <ins>That’s an error.</ins>
<p>Your client has issued a malformed or illegal request. <ins>That’s all we know.</ins>
编辑2-解决方案:
好吧,我有一个解决方案,在比较了与我的谷歌api php客户端示例的输出之后。
简而言之,我的url构造错误。它被构造为一个GET查询(Linkedin就是这样做的,尽管他们的文档声称他们想要POST)
由于这是一个POST请求,我的url应该是
https://accounts.google.com/o/oauth2/token
然后我需要将查询参数作为内容头的一部分。所以主要的工作代码片段是
$params = array('code' => $_GET['code'],
'grant_type' => 'authorization_code',
'redirect_uri' => 'http://carrotleads.com/',
'client_id' => G_CLIENT_ID,
'client_secret' => G_CLIENT_SECRET,
);
$url = 'https://accounts.google.com/o/oauth2/token';
$postBody = http_build_query($params);
$requestHttpContext["content"] = $postBody;
$requestHttpContext["method"] = 'POST';
$options = array( 'http' => $requestHttpContext );
$context = stream_context_create( $options );
echo '<Br>Your request: <pre>'. print_r($url, true) . '</pre>';
echo '<Br>Your options: <pre>'. print_r( $options, true) . '</pre>';
$response = file_get_contents($url, false, $context);
echo 'Your response_data: <pre>'. print_r($response, true) . '</pre>';
其他信息
Google的lib设置了更多的头(来自Google_HttpStreamIO.php)。重要的代码片段转述如下:)
$DEFAULT_HTTP_CONTEXT = array(
"follow_location" => 0,
"ignore_errors" => 1,
);
$DEFAULT_SSL_CONTEXT = array(
"verify_peer" => true,
);
$default_options = stream_context_get_options(stream_context_get_default());
$requestHttpContext = array_key_exists('http', $default_options) ?
$default_options['http'] : array();
$url = 'https://accounts.google.com/o/oauth2/token';
$params = array('code' => $_GET['code'],
'grant_type' => 'authorization_code',
'redirect_uri' => 'http://carrotleads.com/xxxxxxxxxx',
'client_id' => G_CLIENT_ID,
'client_secret' => G_CLIENT_SECRET,
);
$postBody = http_build_query($params);
$postsLength = strlen($postBody);
$requestHeaders = array_merge( array('content-type' => 'application/x-www-form-urlencoded'), array('content-length' => $postsLength));
$headers = "";
foreach($requestHeaders as $k => $v) {
$headers .= "$k: $v'n";
}
$requestHttpContext["header"] = $headers;
$requestHttpContext["content"] = $postBody;
$requestHttpContext["method"] = 'POST';
$requestSslContext = array_key_exists('ssl', $default_options) ?
$default_options['ssl'] : array();
if (!array_key_exists("cafile", $requestSslContext)) {
$requestSslContext["cafile"] = dirname(__FILE__) . '/cacerts.pem';
}
$options = array("http" => array_merge(self::$DEFAULT_HTTP_CONTEXT,
$requestHttpContext),
"ssl" => array_merge(self::$DEFAULT_SSL_CONTEXT,
$requestSslContext));
$context = stream_context_create($options);
$response_data = file_get_contents($url,
false,
$context);
交换Auth令牌不需要所有这些额外的头,但如果您想按照标准进行,那就是方法,包括通过证书文件进行发送。
如果你需要大量往返于Google API的请求,你最好使用编写良好的Google lib,如果不是这样的话,这可能是杀鸡用牛刀,上面的代码片段应该会有所帮助。
事后看来,我觉得自己很傻,因为我没有意识到POST和GET请求的区别,生活和学习;仔细研究了两天后,我松了一口气。
CURL请求可能有问题。在GoogleAnalyticsAPI.class.php > class Http > function curl (around line 720)
中,再添加一个选项来阻止CURL验证对等方的证书:
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
当您向Google请求/o/oauth2/token
时,这必须是POST。仅仅将方法更改为POST是不够的,还需要从查询字符串中删除内容,并实际放入请求的正文中。
我强烈建议使用谷歌提供的PHP库(https://code.google.com/p/google-api-php-client/)它会帮你解决这个问题。
如果你确实需要手动完成,请尝试:
- 从查询字符串中删除从"?"开始的所有内容
- 创建自己的正文,确保对每个值进行URL编码:
"grant_type=" . urlencode($_GET['code']) . (etc..)
- 包括一个
Content-Type: application/x-www-form-urlencoded
头和一个Content-Length: nnn
头(如果PHP没有自动执行后者的话) - 是的,很确定每个授权代码只工作一次,而且它们的使用寿命很短
我用这种方式解决了这个问题,当你使用的是PHP版本,然后是旧的5.5.0
<?php
if(version_compare(PHP_VERSION, '5.5.0', '<')):
function curl_reset($ch){
curl_setopt($ch, CURLOPT_HTTPGET, 1);
curl_setopt($ch, CURLOPT_POST, false);
}
endif;