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

时间:2016-04-20 14:56:22

标签: php arrays api oauth tumblr

我制作了一个简单的脚本,在tumblr上发布图片。 一切都很好,但在我改变主机提供商(我的新主机有限且价格低廉)之后,我发现了一些性能问题。

现在,在调试脚本并联系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));
}

以上这两个函数来自一个在线功能示例,它们一直运行良好。

这是我用来调用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标头:"Authorization: " . $headers['Authorization'],

正如我所说,一切顺利,直到我试图发布6个文件的gif photoset,总共6Mb(tumblr允许每个文件2Mb,总共10Mb)。

PHP耗尽内存并返回错误,这里启动我的调试,一段时间后我联系了tumblr api帮助台,他们以这种方式回答:

  

您不应该将文件包含在用于的参数中   生成oauth签名。有关如何完成此操作的示例,   结帐我们的官方API客户之一。

这会改变一切。直到现在,我将整个参数数组传递给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); 
接缝是合法的,我传递了一个新的数组到函数,然后函数生成oAuth,标题被传回,我可以将它们用于发布调用,结果是:

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

要求更多tumblr api帮助台只能链接到他们的文档和他们的" tumblr php客户端"这是我无法使用的,所以它不是一个选择。

有没有人有oAuth的经验,可以解释一下我做错了什么?据我所知,诀窍是oauth_sig创建的加密数据,但我无法弄清楚如何继续。

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

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

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

1 个答案:

答案 0 :(得分:4)

在挖掘问题几个小时后,调试,检查tumblr api和api客户端,注册测试帐户并尝试发布一些图像。好消息是我最终想出了一个解决方案。它不仅仅使用原生CURL,您需要guzzle和OAuth库来签署请求。

Tumblr伙伴们对签署请求是正确的。您无需传递图像数据即可对请求进行签名。如果你查看他们的官方图书馆,你可以看到; https://github.com/tumblr/tumblr.php/blob/master/lib/Tumblr/API/RequestHandler.php#L85

我试图解决本机CURL库的问题,但遗憾的是我没有成功,要么我以错误的方式签署请求,要么丢失了请求标题,数据等中的内容。我实际上并不知道,Tumblr api非常不好告诉你你做错了什么。

所以我欺骗了一点并开始阅读Tumblr api客户端代码,我想出了一个解决方案。

我们先走了,首先你需要两个包。

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

然后是PHP代码,只需定义你的密钥,令牌,秘密等等。那么它应该是好的。

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

string(70) "{"meta":{"status":201,"msg":"Created"},"response":{"id":143679527674}}" 这是网址; 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://stackoverflow.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());