Twitter API 1.1在搜索包含"@"时返回身份验证错误的性格


Twitter API 1.1 returns authentification error when the search contains the "@" character

如果我将test传递给tweets.json端点的q参数,它会返回良好的数据。但是,如果我在前面包括@符号,即@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参数的前面检索@符号的数据?

正如我所怀疑的,这是一个双重(甚至三重)编码问题。我设法让它以两种不同的方式工作(它们不能一起使用):

  1. 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)
    
  2. http_build_query()之后移除urldecode():

    $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);

微妙的区别是,这次您没有在http_build_query()之后使用urldecode()

由于所有的编码都在进行,您最终用于签名的url与您用于请求的url不匹配,因此身份验证失败。


oauth_signature参数包含一个值,该值是通过签名算法运行所有其他请求参数和两个秘密值生成的。签名的目的是为了让Twitter能够验证请求在传输过程中没有被修改,验证发送请求的应用程序,并验证应用程序是否有权与用户的帐户进行交互。

从授权请求- Twitter开发人员。

这个有效,url被编码2次,签名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

这个没有,url被编码1次,签名url被编码3次:

// end of $url
?q=%40test&count=100
// end of $base_string, used in signature
%26oauth_version%3D1.0%26q%3D%252540test

由于签名只能比请求url多编码一次,因此前面提到的两种解决方案(独立地)都有效,因为:

  • 方案1不编码$query,使$url单编码,签名双编码。
  • 方案二保留双编码,使$url双编码,使签名三编码。

但是等等,为什么它只在使用@时失败?

因为这是所有参数中唯一需要编码的字符。它在使用http_build_query()时进行编码,生成一个%字符,该字符将在随后的编码中被捕获。

相关文章: