PHP 的 oAuth 签名创建问题(将照片集发布到 Tumblr)

oAuth signature creation issue with PHP (posting photoset to Tumblr)

我制作了一个简单的脚本,可以在 tumblr 上 posts 图片。 一切都很好,但我在更改主机提供商后立即注意到一些性能问题(我的新主机有限且便宜)。

现在,在调试脚本并联系 tumblr api 服务台后,我遇到了一个问题:

有3个函数:

function oauth_gen($method, $url, $iparams, &$headers) {

    $iparams['oauth_consumer_key'] = CONSUMER_KEY;
    $iparams['oauth_nonce'] = strval(time());
    $iparams['oauth_signature_method'] = 'HMAC-SHA1';
    $iparams['oauth_timestamp'] = strval(time());
    $iparams['oauth_token'] = OAUTH_TOKEN;
    $iparams['oauth_version'] = '1.0';
    $iparams['oauth_signature'] = oauth_sig($method, $url, $iparams);    
    $oauth_header = array();
    foreach($iparams as $key => $value) {
        if (strpos($key, "oauth") !== false) { 
           $oauth_header []= $key ."=".$value;
        }
    }

    $str = print_r($iparams, true);
    file_put_contents('data1-1.txt', $str); 
    $oauth_header = "OAuth ". implode(",", $oauth_header);
    $headers["Authorization"] = $oauth_header;
}

function oauth_sig($method, $uri, $params) {

    $parts []= $method;
    $parts []= rawurlencode($uri);   
    $iparams = array();
    ksort($params);
    foreach($params as $key => $data) {
            if(is_array($data)) {
                $count = 0;
                foreach($data as $val) {
                    $n = $key . "[". $count . "]";
                    $iparams []= $n . "=" . rawurlencode($val);
                    //$iparams []= $n . "=" . $val;
                    $count++;
                }
            } else {
                $iparams[]= rawurlencode($key) . "=" .rawurlencode($data);
            }
    }
    //debug($iparams,"iparams");
    $str = print_r($iparams, true);
    file_put_contents('data-1.txt', $str);
    //$size = filesize('data.txt');

    $parts []= rawurlencode(implode("&", $iparams));
    //debug($parts,"parts");
    //die();
    $sig = implode("&", $parts);
    return base64_encode(hash_hmac('sha1', $sig, CONSUMER_SECRET."&". OAUTH_SECRET, true));
}

上面这2个函数来自于网上的一个函数示例,一直运行良好。

这是我用来调用 API 和 oAuth 的函数:

function posta_array($files,$queue,$tags,$caption,$link,$blog){
    $datArr = array();
    $photoset_layout = "";
    foreach ($files as $sing_file){
        $dataArr [] = file_get_contents($sing_file);
        $photoset_layout .= "1";
    } 

    $headers = array("Host" => "http://api.tumblr.com/", "Content-type" => "application/x-www-form-urlencoded", "Expect" => "");

    $params = array(
        "data" => $dataArr,
        "type" => "photo",
        "state" => $queue,
        "tags"=>$tags,
        "caption"=>$caption,
        "photoset_layout" => $photoset_layout,
        "link"=>str_replace("_","",$link)
    );
    debug($headers,"head");
    oauth_gen("POST", "http://api.tumblr.com/v2/blog/$blog/post", $params, $headers);
    debug($headers,"head 2");
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_USERAGENT, "Tumblr v1.0");
    curl_setopt($ch, CURLOPT_URL, "http://api.tumblr.com/v2/blog/$blog/post");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1 );
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        "Authorization: " . $headers['Authorization'],
        "Content-type: " . $headers["Content-type"],
        "Expect: ")
    );
    $params = http_build_query($params);
    $str = print_r($params, true);
    file_put_contents('data_curl1.txt', $str);


    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
    $response = curl_exec($ch);
    debug($response,"response");
    return $response;

}

这个函数有点问题,我试着解释一下:

我调用了 oauth_gen 将参数数组传递给它,oauth_gen 创建了我后来在这里使用的 oauth header:"Authorization: " . $headers['Authorization'],.

正如我所说,一切都很顺利,直到我尝试 post 一个包含 6 个文件的 gif 照片集,总共 6Mb(tumblr 允许每个文件 2Mb,总共 10Mb)。

PHP 内存不足和 return 错误,这里开始我的调试,过了一会儿我联系了 tumblr api 帮助台,他们是这样回答的:

You shouldn't need to include the files in the parameters used for generating the oauth signature. For an example of how this is done, checkout one of our official API clients.

这改变了一切。直到现在,我将整个参数数组传递给 oauth_gen,它调用 oauth_sig,将所有内容原始编码到数组中(包括 gif 文件的二进制字符串),结果是二进制文件大约 1Mb 变成至少 3Mb 的 rawurlencoded 字符串。

这就是我出现记忆问题的原因。很好,所以,正如服务台所说,我已经以这种方式更改了对 oauth_gen 的调用:

$new_array = array();
oauth_gen("POST", "http://api.tumblr.com/v2/blog/$blog/post", $new_array, $headers); 

seams 对我来说是合法的,我将一个新数组传递给函数,然后函数生成 oAuth,headers 被传回,我可以将它们用于 posting 调用,结果是:

{"meta":{"status":401,"msg":"Unauthorized"},"response":[]}

向 tumblr api 帮助台询问更多内容只会导致更多链接到他们的文档和他们的 "tumblr php client" 我无法使用,所以它不是一个选项。

有没有人有使用 oAuth 的经验并且可以解释我做错了什么?据我所知,诀窍在于 oauth_sig 创建的加密数据,但我不知道如何继续。

我真的很想了解 oauth,但我阅读了更多关于它的内容以及更多 tumblr helpdsek 接缝对我来说,但是......解决方案不起作用,只有当我让 oauth 功能加密整个数据阵列(包括图像和所有内容),但我可以理解这是错误的...帮助我。

更新 1 我今天尝试了一个新东西,首先我创建了一个空数组,然后通过引用传递给 oauth_gen 并且只有在生成签名之后,我才将关于 [= 的所有其他字段添加到同一个数组中69=]本身,但结果是一样的。

更新 2 在这里阅读:http://oauth.net/core/1.0a/#signing_process 似乎请求的参数必须全部用于签名,但这并不完全清楚(如果有人能更好地解释它,我真的很感激)。 这很奇怪,因为如果它是真的,那就违背了 Tumblr 服务台的话,而如果不是真的,整个过程就会有点混乱。 顺便说一句,此时,我对同一点感到震惊。

在深入研究这个问题几个小时后,调试、查看 tumblr api 和 api 客户端,注册一个测试帐户并尝试 post 一些图像。好消息是我终于想出了一个解决方案。它不仅仅使用本机 CURL,您需要 guzzle 和 OAuth 库来签署请求。

Tumblr 的家伙们关于签署请求是正确的。您不需要传递图像数据来签署请求。如果您查看他们的官方图书馆,您会看到; https://github.com/tumblr/tumblr.php/blob/master/lib/Tumblr/API/RequestHandler.php#L85

我尝试使用本机 CURL 库解决问题,但不幸的是我没有成功,要么我以错误的方式签署请求,要么在请求 header、数据等中遗漏了某些内容。我没有实际上我不知道,Tumblr api 真的很不善于告诉你你做错了什么。

所以我有点作弊并开始阅读 Tumblr api 客户端代码,然后我想出了一个解决方案。

开始吧,首先你需要两个包。

$ composer require "eher/oauth:1.0.*"
$ composer require "guzzle/guzzle:>=3.1.0,<4"

然后是 PHP 代码,只需定义您的密钥、令牌、机密等。然后就可以开始了。

由于签名请求不包含图片数据,因此没有超出内存限制。在签署请求后,实际上我们并没有将文件的内容放入我们的 post 数据数组中。我们正在使用 guzzle 的 addPostFiles 方法,它负责将文件添加到 POST 请求,为您完成脏活。这是我的结果;

string(70) "{"meta":{"status":201,"msg":"Created"},"response":{"id":143679527674}}" 这是 url; http://blog-transparentcoffeebouquet.tumblr.com/

<?php
ini_set('memory_limit', '64M');

define("CONSUMER_KEY", "");
define("CONSUMER_SECRET", "");
define("OAUTH_TOKEN", "");
define("OAUTH_SECRET", "");

function request($options,$blog) {

    // Take off the data param, we'll add it back after signing
    $files = isset($options['data']) ? $options['data'] : false;
    unset($options['data']);

    $url = "https://api.tumblr.com/v2/blog/$blog/post";

    $client =  new \Guzzle\Http\Client(null, array(
        'redirect.disable' => true
    ));

    $consumer = new \Eher\OAuth\Consumer(CONSUMER_KEY, CONSUMER_SECRET);
    $token = new \Eher\OAuth\Token(OAUTH_TOKEN, OAUTH_SECRET);

    $oauth = \Eher\OAuth\Request::from_consumer_and_token(
        $consumer,
        $token,
        "POST",
        $url,
        $options
    );
    $oauth->sign_request(new \Eher\OAuth\HmacSha1(), $consumer, $token);
    $authHeader = $oauth->to_header();
    $pieces = explode(' ', $authHeader, 2);
    $authString = $pieces[1];

    // POST requests get the params in the body, with the files added
    // and as multipart if appropriate
    /** @var \Guzzle\Http\Message\RequestInterface $request */
    $request = $client->post($url, null, $options);
    $request->addHeader('Authorization', $authString);
    if ($files) {
        if (is_array($files)) {
            $collection = array();
            foreach ($files as $idx => $f) {
                $collection["data[$idx]"] = $f;
            }
            $request->addPostFiles($collection);
        } else {
            $request->addPostFiles(array('data' => $files));
        }
    }


    $request->setHeader('User-Agent', 'tumblr.php/0.1.2');

    // Guzzle throws errors, but we collapse them and just grab the
    // response, since we deal with this at the \Tumblr\Client level
    try {
        $response = $request->send();
    } catch (\Guzzle\Http\Exception\BadResponseException $e) {
        $response = $request->getResponse();
    }

    // Construct the object that the Client expects to see, and return it
    $obj = new \stdClass;
    $obj->status = $response->getStatusCode();
    $obj->body = $response->getBody();
    $obj->headers = $response->getHeaders()->toArray();

    return $obj;
}

$files = [
    "/photo/1.jpg",
    "/photo/2.jpg",
    "/photo/3.png",
    "/photo/4.jpg",
    "/photo/1.jpg",
    "/photo/2.jpg",
    "/photo/3.png",
    "/photo/4.jpg",
    "/photo/1.jpg",
    "/photo/2.jpg",
];

$params = array(
    "type" => "photo",
    "state" => "published",
    "tags"=> [],
    "caption"=>"caption",
    "link"=>str_replace("_","","http://whosebug.com/questions/36747697/oauth-signature-creation-issue-with-php-posting-photoset-to-tumblr"),
    "data" => $files,
);


$response = request($params, "blog-transparentcoffeebouquet.tumblr.com");
var_dump($response->body->__toString());