无法使用 Twitter V1.1 验证用户 API
Can not authenticate user with Twitter V1.1 API
我已经实现了 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 );
}
我使用这个函数来构建签名基础字符串
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 );
}
我用这个函数来计算签名
function calculate_signature( $signature_base_string, $signing_key ){
return base64_encode( hash_hmac('sha1', $signature_base_string, $signing_key, true) );
}
现在开始构建 OAuth Header。这是它的一个函数,它使用上面的辅助函数(加上一些其他 return 所需信息):
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_nonce' => $credentials->nonce,
'oauth_signature' => $signature,
'oauth_signature_method' => 'HMAC-SHA1',
'oauth_timestamp' => $credentials->time,
'oauth_token' => $oAuth->oauth_token,
'oauth_version' => '1.0'
);
ksort( $auth_array );
return $auth_array;
}
现在我已经把所有东西都放在一个漂亮整洁的小阵列中了,是时候实际尝试将它发送到 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, CURLOPT_URL, $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;
}
}
例如,这是 OAuth header 中所有内容的顺序。我 运行 遍历了我的每个小辅助函数十几次,以确保它们接受正确的参数并输出适当的值。我什至用文档中的 OAuth 凭据替换了我自己的 OAuth 凭据,并最终得到了与签名密钥、签名等相同的结果:
然而,每次我尝试 运行 create_tweet()
函数时,我都会收到错误 32 的 401 状态代码:{"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
传递给签名基础字符串函数,return如下:
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
这看起来不错,现在我使用签名密钥: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 帐户来测试所有这些,但他们的文档有我能够使用的示例密钥和示例输出,并且使用这些更改和文档产生了相同的输出。
我已经实现了 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 );
}
我使用这个函数来构建签名基础字符串
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 );
}
我用这个函数来计算签名
function calculate_signature( $signature_base_string, $signing_key ){
return base64_encode( hash_hmac('sha1', $signature_base_string, $signing_key, true) );
}
现在开始构建 OAuth Header。这是它的一个函数,它使用上面的辅助函数(加上一些其他 return 所需信息):
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_nonce' => $credentials->nonce,
'oauth_signature' => $signature,
'oauth_signature_method' => 'HMAC-SHA1',
'oauth_timestamp' => $credentials->time,
'oauth_token' => $oAuth->oauth_token,
'oauth_version' => '1.0'
);
ksort( $auth_array );
return $auth_array;
}
现在我已经把所有东西都放在一个漂亮整洁的小阵列中了,是时候实际尝试将它发送到 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, CURLOPT_URL, $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;
}
}
例如,这是 OAuth header 中所有内容的顺序。我 运行 遍历了我的每个小辅助函数十几次,以确保它们接受正确的参数并输出适当的值。我什至用文档中的 OAuth 凭据替换了我自己的 OAuth 凭据,并最终得到了与签名密钥、签名等相同的结果:
然而,每次我尝试 运行 create_tweet()
函数时,我都会收到错误 32 的 401 状态代码:{"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
传递给签名基础字符串函数,return如下:
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
这看起来不错,现在我使用签名密钥: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 帐户来测试所有这些,但他们的文档有我能够使用的示例密钥和示例输出,并且使用这些更改和文档产生了相同的输出。