如果我将test
传递给q
端点的tweets.json
参数,则会返回正确的数据。但如果我在前面加@
符号,即@test
,我会收到以下错误:
无法验证您的身份。
当我使用%40
代替@
时会发生同样的问题。
这是我的代码:
$query = array( // query parameters
'q' => '@test',
'count' => '100'
);
$method = "GET";
$path = "/1.1/search/tweets.json";
$token = 'xxxxxx';
$token_secret = 'xxxxxx';
$consumer_key = 'xxxxxxx';
$consumer_secret = 'xxxxxx';
$host = 'api.twitter.com';
$oauth = array(
'oauth_consumer_key' => $consumer_key,
'oauth_token' => $token,
'oauth_nonce' => (string)mt_rand(), // a stronger nonce is recommended
'oauth_timestamp' => time(),
'oauth_signature_method' => 'HMAC-SHA1',
'oauth_version' => '1.0'
);
$oauth = array_map("rawurlencode", $oauth); // must be encoded before sorting
$query = array_map("rawurlencode", $query);
$arr = array_merge($oauth, $query); // combine the values THEN sort
asort($arr); // secondary sort (value)
ksort($arr); // primary sort (key)
// http_build_query automatically encodes, but our parameters
// are already encoded, and must be by this point, so we undo
// the encoding step
$querystring = urldecode(http_build_query($arr, '', '&'));
$url = "https://$host$path";
// mash everything together for the text to hash
$base_string = $method."&".rawurlencode($url)."&".rawurlencode($querystring);
// same with the key
$key = rawurlencode($consumer_secret)."&".rawurlencode($token_secret);
// generate the hash
$signature = rawurlencode(base64_encode(hash_hmac('sha1', $base_string, $key, true)));
// this time we're using a normal GET query, and we're only encoding the query params
// (without the oauth params)
$url .= "?".http_build_query($query);
$url=str_replace("&","&",$url); //Patch by @Frewuill
$oauth['oauth_signature'] = $signature; // don't want to abandon all that work!
ksort($oauth); // probably not necessary, but twitter's demo does it
// also not necessary, but twitter's demo does this too
function add_quotes($str) { return '"'.$str.'"'; }
$oauth = array_map("add_quotes", $oauth);
// this is the full value of the Authorization line
$auth = "OAuth " . urldecode(http_build_query($oauth, '', ', '));
// if you're doing post, you need to skip the GET building above
// and instead supply query parameters to CURLOPT_POSTFIELDS
$options = array( CURLOPT_HTTPHEADER => array("Authorization: $auth"),
//CURLOPT_POSTFIELDS => $postfields,
CURLOPT_HEADER => false,
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false);
// do our business
$feed = curl_init();
curl_setopt_array($feed, $options);
$json = curl_exec($feed);
curl_close($feed);
return $json;
为什么我无法使用@
参数前面的q
符号检索数据?
答案 0 :(得分:2)
我怀疑,这是一个双重(甚至三重)编码问题。我设法让它以两种不同的方式工作(它们不能一起使用):
在cut-off > 0.90
之前合并,我的首选解决方案:
rawurlencode()
在//NOPE $oauth = array_map("rawurlencode", $oauth); // must be encoded before sorting
//NOPE $query = array_map("rawurlencode", $query);
$arr = array_merge($oauth, $query); // combine the values THEN sort
$arr = array_map("rawurlencode", $arr);
asort($arr); // secondary sort (value)
ksort($arr); // primary sort (key)
后删除urldecode()
:
http_build_query()
但是在这一点上,我无法解释为什么它们中的任何一个都有效。事实证明,在代码的后面,你使用它:
$querystring = (http_build_query($arr, '', '&'));
微妙的区别在于您这次// this time we're using a normal GET query, and we're only encoding the query params
// (without the oauth params)
$url .= "?".http_build_query($query);
之后没有使用urldecode()
。
由于所有进行的编码,您最终用于签名的网址与您用于请求的网址不匹配,因此无法进行身份验证。
<强>特征强>
http_build_query()
参数包含通过签名算法运行所有其他请求参数和两个秘密值而生成的值。签名的目的是使Twitter可以验证请求在传输过程中未被修改,验证发送请求的应用程序,并验证应用程序是否有权与用户的帐户进行交互。
来自Authorizing a request - Twitter Developers。
这样做,url编码2次,签名url编码3次:
oauth_signature
这不,url编码1次,签名url编码3次:
// end of $url, encoded twice
?q=%2540test&count=100
// end of $base_string, used in signature, encoded thrice
%26oauth_version%3D1.0%26q%3D%252540test
因为签名只能比请求网址再编码一次,所以前面提到的解决方案都可以(独立地)工作,因为:
// end of $url
?q=%40test&count=100
// end of $base_string, used in signature
%26oauth_version%3D1.0%26q%3D%252540test
进行编码,使$query
单一编码,签名双重编码。$url
双重编码并使签名三重编码。但是等等,为什么在使用$url
?
因为这是实际需要编码的所有参数中唯一的字符。它在使用@
时进行编码,生成http_build_query()
字符,该字符将在后续编码中被捕获。