【问题标题】:Python POST request same JS/PHPPython POST 请求相同的 JS/PHP
【发布时间】:2014-09-03 10:33:56
【问题描述】:

我不擅长js/php,但我需要将下面的代码转换成Python。

Javascript 版本:

function api_query(key,keyId,url,post,body,signature,cb){var method='POST';
    var date=new Date();
    var content_type='application/x-www-form-urlencoded; charset=UTF-8;';
    var body_md5=CryptoJS.MD5(body);
    var http= new XMLHttpRequest();http.onreadystatechange=function(){if (http.readyState===4){cb(JSON.parse(http.responseText));}};
    http.open("POST",'http://'+url,true);http.setRequestHeader('x-pbx-date',date);
    http.setRequestHeader('Accept','application/json');
    http.setRequestHeader('Content-Type',content_type);
    http.setRequestHeader('Content-MD5',body_md5);
    http.setRequestHeader('x-pbx-authentication',keyId+':'+signature);
    http.send(body);}

PHP版本:

    $post = http_build_query($post);
    $content_type = 'application/x-www-form-urlencoded';
    $content_md5 = hash('md5', $post);
    $signature = base64_encode(hash_hmac('sha1', $method."\n".$content_md5."\n".$content_type."\n".$date."\n".$url."\n", $secret_key, false));
    $headers = array('Date: '.$date, 'Accept: application/json', 'Content-Type: '.$content_type, 'x-pbx-authentication: '.$key_id.':'.$signature, 'Content-MD5: '.$content_md5);

    if (isset($opt['secure']) && $opt['secure']){
        $proto = 'https';
    }else{
        $proto = 'http';
    }
    $ch = curl_init($proto.'://'.$url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_TIMEOUT, 60);
    $res = json_decode(curl_exec($ch), true);
    if ($res){return $res;}else{return false;}

我尝试过请求库:

headers = {
    'x-pbx-date': date,
    'Content-Type': content_type,
    'Content-MD5': body_md5_str,
    'x-pbx-authentication': signature,
}
payload = {
    'date_from': date_from,
    'date_to': date_to,
}
r = requests.post(url, data=json.dumps(payload), headers=headers)

httlib2:

http = httplib2.Http()
response, content = http.request(url, 'POST', headers=headers, body=urllib.urlencode(payload))

urllib2:

req = urllib2.Request(url, data=urllib.urlencode(payload), headers=headers)
response = urllib2.urlopen(req).read()

没有任何作用。在每次尝试服务器响应中:未通过身份验证。有任何想法吗?

【问题讨论】:

    标签: javascript php python http-post


    【解决方案1】:

    好吧,我猜你的问题是身份验证。你没有展示你是如何在 Python 中做到这一点的,但我想这是有问题的,因为你在有身体之前就以某种方式计算了 body_md5_strsignaturejson.dumps 调用),所以一定有什么问题关于它。您是否确保您的代码生成与 JS 和 PHP 代码相同的值?

    就我个人而言,我会实现一个身份验证类,它会在请求触发之前添加必要的标头。有关详细信息,请参阅requests documentation on custom auth,如果我的详细信息正确,这是我(未经测试)的尝试:

    import requests
    import hashlib
    import hmac
    import base64
    from wsgiref.handlers import format_date_time
    import datetime
    from time import mktime
    
    CONTENT_TYPE_FORM_URLENCODED = "application/x-www-form-urlencoded"
    
    class OnlinePBXAuth(requests.auth.AuthBase):
        def __init__(self, key_id, secret):
            self.key_id = key_id
            self.secret = secret
    
        def __call__(self, r):
            content_type = r.headers.get("Content-Type", CONTENT_TYPE_FORM_URLENCODED)
            body = r.body or ""
            body_md5 = hashlib.md5(body).hexdigest()
            date = format_date_time(mktime(datetime.datetime.now().timetuple()))
            date = r.headers.get("Date", date)
            r.headers["Date"] = date
            r.headers["X-PBX-Date"] = date
            sign = "\n".join([r.method, body_md5, content_type, date, r.url, ""])
            signature = base64.b64encode(hmac.new(self.secret, sign, hashlib.sha1).hexdigest())
            r.headers["Content-MD5"] = body_md5
            r.headers["X-PBX-Authentication"] = "{0}:{1}".format(self.key_id, signature)
            return r
    

    不是最漂亮的代码,但我想这应该可以解决问题。

    然后,像这样使用它:

    auth = OnlinePBXAuth(KEY_ID, KEY_SECRET)
    ...
    data = {"date_from": date_from, "date_to": date_to}
    r = requests.post(url, data, auth=auth)
    ...
    

    或者甚至可能是这样的:

    session = requests.Session(auth=OnlinePBXAuth(KEY_ID, KEY_SECRET))
    ...
    r1 = session.post(url, data)
    ...
    r2 = session.post(another_url, another_data)
    

    我不确定它是否适用于空体请求(就像大多数 GET 一样)和多部分分块体(所以请自行计算或尝试避免这些),但我认为它应该适用于应用程序/x-www-form-urlencoded 和 application/json 编码的数据。

    【讨论】:

    • 是的,我绝对确保我的代码生成与 php/js 在其他情况下相同的值。我是通过 Chrome 逐步调试找到的。相同的 md5 哈希和签名。您的代码返回:{"status":0,"comment":"not authenticated","data":""}Api examples and php/js library links in Russian
    • 嗯...那我猜我错了。对不起。不幸的是,我看不出代码有什么问题。好吧,有一个服务RequestBin 可以显示对它发出的 HTTP 请求。也许值得尝试将 PHP 和 Python 程序(具有固定日期,因此请求完全相同)都指向那里并将请求与相同数据进行比较?也许它会提供一些线索,Python 版本可能有什么问题。
    • 哦,在我的代码中生成的 signature_str 中有一次 '\n' 符号。所有方法,包括您的工作。泰寻求帮助。
    猜你喜欢
    • 1970-01-01
    • 2020-04-05
    • 2019-11-04
    • 1970-01-01
    • 2021-03-11
    • 1970-01-01
    • 2019-04-26
    • 1970-01-01
    • 2017-08-19
    相关资源
    最近更新 更多