为什么我无法使用 OAuth 进行身份验证


Why can't I authenticate with OAuth?

我写了一个非常简单的应用程序来更新我的推特状态。我已经使用Twitter文档来了解创建OAuth签名的要求以及如何构建授权标头。然后,我在 PHP 中使用 cURL 发送请求。

使用 Twitter 开发站点上的 OAuth 工具,我比较了我的签名基础字符串和授权标头,两者完全相同:

签名基字符串

POST&https%3A%2F%2Fapi.twitter.com%2F1%2Fstatuses%2Fupdate.json&oauth_consumer_key%3DYNxxxxxxxxxxxWnfI6HA%26oauth_nonce%3D31077a3c7b7bee4e4c7e2b5185041c12%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1340729904%26oauth_token%3D2991771-4csoiO2fxmWgSxxxxxxxxxxDjWj2AbyxATtiuadNE%26oauth_version%3D1.0%26status%3Dblah%2520test%2520blah.

授权标头

Authorization: OAuth oauth_consumer_key="YN4FLBxxxxxxxxxxI6HA", oauth_nonce="31077a3c7b7bee4e4c7e2b5185041c12", oauth_signature="M2cXepcxxxxxxxxxxAImeAjE%2FHc%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1340729904", oauth_token="2991771-4cxxxxxxxxxxSmRvjzMoooMDjWj2AbyxATtiuadNE", oauth_version="1.0"

显然,我用x替换了一些字符来隐藏我的数据,但是比较两个字符的字符会产生完全相同的结果。作为参考,我对 OAuth 工具生成的时间戳和随机数进行了硬编码,以便我的值可以与检查时相同。我的访问权限级别设置为"读取和写入"。在同一页面上,还有最后一个示例 - 在命令行上使用 cURL 运行的命令。当我运行此命令时,它运行良好,并且可以毫无问题地发布到我的推特提要。

考虑到这一点,我相信到目前为止我创建的所有内容都很好,并且认为我发布生成前面提到的细节的代码没有多大意义。但是,我认为我用来调用cURL的代码是罪魁祸首,但无法说出原因:

<?php
// ...
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $baseUrl);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_HTTPHEADER, array("Authorization: $header"));
curl_setopt($curl, CURLOPT_POSTFIELDS, array('status' => $status));
$result = json_decode(curl_exec($curl));
curl_close($curl);
var_dump($result);

请注意,$baseUrl$header$status 是用于生成签名基字符串和授权标头的相同变量,它们匹配得很好。

运行时页面的输出为:

object(stdClass)#1 (2) { ["error"]=> string(34) "Could not authenticate with OAuth." ["request"]=> string(23) "/1/statuses/update.json" }

我希望这里有足够的细节,有人为我指出正确的方向!

经过更多的搜索、apache_request_headers()测试并坚持我的数据很好并且问题所在是 cURL 的概念,我意识到 cURL 将请求的Content-type设置为multipart/form-data;并添加边界信息,显然也具有更长的Content-Length字段。这意味着状态没有正确发送,我认为是因为格式不正确的multipart/form-data;请求。

解决方案是将其作为字符串发送。例如,这有效:

curl_setopt($curl, CURLOPT_POSTFIELDS, 'status='. rawurlencode($status));

但我发现有一种更好的方法(尤其是当我想使用数组时,有多个值):

$postfields = array('status' => $status);
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($postfields));

恕我直言,这看起来更好。

我认为这是你的随机数。 来自文档:"oauth_nonce参数是应用程序应为每个唯一请求生成的唯一令牌"(强调我的)。

警告:我更熟悉OAuth 2 + Java或JavaScript,而不是OAuth 1 + PHP。

如果不是这样(或者不是唯一的),您可以将实际的HTTP请求(例如使用WireShark)与他们在该页面上记录的示例请求进行比较。 关于"构建标头字符串"的注释也可能有所帮助。