【问题标题】:oAuth signature creation issue with PHP (posting photoset to Tumblr)oAuth 签名创建问题与 PHP(将照片集发布到 Tumblr)
【发布时间】:2016-08-13 08:41:03
【问题描述】:

我制作了一个简单的脚本,可以在 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 照片集,总共 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); 

seams 对我来说是合法的,我将一个新数组传递给函数,然后函数生成 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 帮助台的话,而如果它不是真的,整个过程就会有点混乱。 顺便说一句,此时我也被同一点击中了。

【问题讨论】:

    标签: php arrays api oauth tumblr


    【解决方案1】:

    在研究了几个小时后,调试,审查了 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 代码,只需定义您的密钥、令牌、秘密等。然后就可以开始了。

    由于签名请求不包含图片数据,所以不超过内存限制。实际上,在签署请求后,我们并没有将文件的内容放入我们的发布数据数组中。我们正在使用addPostFiles guzzle 方法,它负责将文件添加到 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());
    

    【讨论】:

    • 嗨,Ugur,感谢您的回答,在尝试使用 curl 处理整个过程之前,我也已经介入了您的道路,实际上我在谷歌的 tumblr 开发组中谈论这个问题,最后我明白这完全是关于“你如何将参数发送到tumblr”的问题,使用多部分表单数据,你不能在签名中包含参数,同时使用 application/x-www-form-urlencoded 之类的我愿意,签名必须包含每个参数,我会为你的问题投票,但我会在解决问题后编写整个过程。
    • 嗨,Matteo,感谢您的评论和投票。为了更具描述性,我不会使用整个原生 curl 方法,Guzzle 会处理您是否必须发送“application/x-www-form-urlencoded”或“multipart/form-data”。我不知道您在 Tumblr Google 群组中讨论什么,但如果您的目标是向 Tumblr 发送帖子,我相信我的方法能够解决问题。我不明白你为什么只坚持“application/x-www-form-urlencoded”?你有什么限制吗?另外我相信使用 Guzzle、Eher/OAuth 等软件包有助于开发稳定的软件。
    • Hey Ugur,是的,你的方法实际上很好,我遇到的问题是我不能将任何我没有编写或完全阅读的代码放在服务器上,所以使用库和预制是有点困难,因为我必须记录所有函数、类和方法,这就是为什么我喜欢更简单的手动卷曲方法。其实你是对的,我会接受你的回答,我会把我的解决方案直接写到问题中,这样就可以涵盖所有未来的案例。
    • @Ant100 你好!关于令牌和 oAuth 的一切都是从行开始的; ´$consumer = new \Eher\OAuth\Consumer(CONSUMER_KEY, CONSUMER_SECRET); $token = new \Eher\OAuth\Token(OAUTH_TOKEN, OAUTH_SECRET);´ 并以行结尾,´$request->addHeader('Authorization', $authString);´ Eher\OAuth 库负责处理令牌并生成服务的授权标头。您可能放错了标记,这对我来说是回答这个问题时的常见陷阱。
    • @m.qayyum 如果我没有误读我自己的代码,我将这些文件放在我的文件系统的根目录下。我认为您可以在定义 $files 数组时使用绝对路径来定位文件。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-20
    • 1970-01-01
    • 1970-01-01
    • 2012-02-19
    • 2016-10-23
    • 2023-03-07
    相关资源
    最近更新 更多