问题描述
我已经实现了Creating a Signature的所有内容。我创建了一个收集所需参数的函数(为清楚起见,在此添加了一些注释):
function collect_parameters(){ global $credentials; // This is an Object with my App's credentials and stuff $oAuth = get_user_oauth(); // This returns an object with the the personal user OAuth tokens retrieved from the earlier docs. $encoded_collection = array(); $collection = array( 'status' => rawurlencode( $_GET['tweet'] ),'include_entities' => 'true','oauth_consumer_key' => $credentials->key,'oauth_nonce' => $credentials->nonce,// md5( str_shuffle( uniqid() . mt_rand(0,9999999999) ) ) 'oauth_signature_method' => 'HMAC-SHA1','oauth_timestamp' => $credentials->time,// current timestamp 'oauth_token' => $oAuth->oauth_token,'oauth_version' => '1.0',); // Percent encode every key and value that will be signed. foreach( $collection as $key => $value ){ $encoded_collection[rawurlencode($key)] = rawurlencode($value); } // Sort the list of parameters alphabetically by encoded key. ksort( $encoded_collection ); return http_build_query( $encoded_collection ); }
@H_404_5@我使用此函数来构建签名基本字符串
function create_signature_base_string( $parameter_string,$url = 'https://api.twitter.com/1.1/statuses/update.json',$method = 'POST' ){ return strtoupper( $method ) .'&'. rawurlencode( $url ) .'&'. rawurlencode( $parameter_string ); }
@H_404_5@我使用此功能来计算签名
function calculate_signature( $signature_base_string,$signing_key ){ return base64_encode( hash_hmac('sha1',$signature_base_string,$signing_key,true) ); }
@H_404_5@现在要构建OAuth标头。这是一个函数,它使用上面的辅助函数(以及其他一些返回所需信息的函数):
function get_oauth_headers(){ global $credentials; $oAuth = get_user_oauth(); $parameters = collect_parameters(); $signature_base_string = create_signature_base_string( $parameters ); $signing_key = get_signing_key(); $signature = calculate_signature( $signature_base_string,$signing_key ); $auth_array = array( 'oauth_consumer_key' => $credentials->key,'oauth_signature' => $signature,'oauth_signature_method' => 'HMAC-SHA1','oauth_token' => $oAuth->oauth_token,'oauth_version' => '1.0' ); ksort( $auth_array ); return $auth_array; }
@H_404_5@现在,我将所有内容都整理得井井有条,现在是时候尝试将其发送到Twitter了。
function create_tweet( $build_query = true ){ global $credentials; $ch = curl_init(); $url = 'https://api.twitter.com/1.1/statuses/update.json'; $fields = array( 'status' => rawurlencode( $_GET['tweet'] ) // I've just been using "Test" or "WhyNowork" style text in this $_GET param ); $oAuth_headers = get_oauth_headers(); // This uses that function above that returns all of the specific parameters for OAuth,sorted,and ready to go. $oAuth_array = array(); // Loop through the oauth headers,and encode them foreach( $oAuth_headers as $key => $value ){ $oAuth_array[] = rawurlencode($key) .'="'. rawurlencode($value) .'"'; } // Implode it into a single line $oAuth_string = implode(',',$oAuth_array ); $headers = array( 'Content-Type: application/x-www-form-rawurlencoded','Authorization: OAuth '. $oAuth_string,); curl_setopt( $ch,CURLOPT_HTTPHEADER,$headers ); curl_setopt( $ch,CURLOPT_RETURNTRANSFER,true ); curl_setopt( $ch,CURLOPT_ENCODING,'gzip' ); curl_setopt( $ch,CURLOPT_POST,true ); // It seems to prefer this as a query string instead of postfields? if( $build_query == true ){ curl_setopt( $ch,CURLOPT_URL,$url.'?'.http_build_query($fields) ); } else { curl_setopt( $ch,$url ); curl_setopt( $ch,CURLOPT_POSTFIELDS,$fields ); } $result = curl_exec( $ch ); $info = curl_getinfo( $ch ); curl_close( $ch ); if( isset($_GET['debug']) ){ echo $result; var_dump( $info ); } else { echo $result; } }
@H_404_5@例如,这是OAuth标头中所有内容的顺序。我已经遍历了我的每个小助手函数十多次,以确保它们接受正确的参数并输出适当的值。我什至用文档中的OAuth凭据替换了自己的OAuth凭据,并最终获得了与它们对签名密钥,签名等相同的结果:
但是,每次尝试运行
create_tweet()
函数时,我都会收到401状态代码,错误32:{"errors":[{"code":32,"message":"Could not authenticate you."}]}
。我到底想念什么?可能会看到为什么他们无法对请求进行身份验证?
这是
collect_parameters()
的输出;include_entities=true&oauth_consumer_key=APP_API_KEY&oauth_nonce=ABC123&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1597456781&oauth_token=USER_AUTH_TOKEN&oauth_version=1.0&status=TESTING
传递给Signature Base String函数,该函数返回以下内容:
POST&https%3A%2F%2Fapi.twitter.com%2F1.1%2Fstatuses%2Fupdate.json&include_entities%3Dtrue%26oauth_consumer_key%3DAPP_API_KEY%26oauth_nonce%3DABC123%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1597457034%26oauth_token%3DUSER_AUTH_TOKEN%26oauth_version%3D1.0%26status%3DTESTING
@H_404_5@这看起来不错,现在我使用签名密钥:
APP_SECRET&USER_AUTH_SECRET
并传递这些签名密钥来计算签名,这给了我一个与文档中的值相同的值(并且在文档中使用参数使我获得了相同的值)他们显示的签名):thIsiSeEmSUnNecEssArYPOs3OxQdSNpI=
我不明白如何用测试数据替换数据并获得相同的结果,但是我仍然无法对API请求进行身份验证?
解决方法
您正在执行几个额外的编码。
首先,在
collect_parameters
中,您要对$encoded_collection
数组的键和值进行编码,然后将其传递给http_build_query
,这将进一步对其进行编码。您可以完全删除循环以对项目进行编码,而直接将其传递给http_build_query
。诀窍在于它默认为+
编码,因此您需要使用第四个参数告诉它切换为%
编码:function collect_parameters() { global $credentials; // This is an Object with my App's credentials and stuff $oAuth = get_user_oauth(); // This returns an object with the the personal user OAuth tokens retrieved from the earlier docs. $collection = [ 'status' => 'Hello Ladies + Gentlemen,a signed OAuth request!','include_entities' => 'true','oauth_consumer_key' => $credentials->key,'oauth_nonce' => $credentials->nonce,// md5( str_shuffle( uniqid() . mt_rand(0,9999999999) ) ) 'oauth_signature_method' => 'HMAC-SHA1','oauth_timestamp' => $credentials->time,// current timestamp 'oauth_token' => $oAuth->oauth_token,'oauth_version' => '1.0',]; // Sort the list of parameters alphabetically by encoded key. ksort($collection); return http_build_query($collection,'','&',PHP_QUERY_RFC3986); }
接下来,在您的
create_tweet
函数的第一个循环中,您将再次对键和值进行编码,这是不需要的,可以将其删除:foreach ($oAuth_headers as $key => $value) { $oAuth_array[] = $key . '="' . $value . '"'; }
不幸的是,我没有一个Twitter帐户可以测试所有这些内容,但是他们的文档具有我可以使用的示例密钥和示例输出,并使用这些更改和文档产生了相同的输出。