代码之家  ›  专栏  ›  技术社区  ›  pjz

如何在Auth对象的调用方法中对requests.Request的主体签名?

  •  0
  • pjz  · 技术社区  · 6 年前

    我想为kraken写一个不错的auth助手。我希望它尽可能自动,所以它需要:

    1. time.time()*1000 )到柱体
    2. 在邮件正文上计算签名
    3. 把签名放在信头上

    我根据 this

    class KrakenAuth(AuthBase):                                                                                                                                         
        """a requests-module-compatible auth module for kraken.com"""                                                                                                                  
        def __init__(self, key, secret):                                                                                                                                
            self.api_key    = key                                                                                                                                       
            self.secret_key = secret                                                                                                                                    
    
        def __call__(self, request):                                                                                                                                    
            #print("Auth got a %r" % type(request))                                                                                                                      
            nonce = int(1000*time.time())                                                                                                                               
            request.data = getattr(request, 'data', {})                                                                                                                 
            request.data['nonce'] = nonce                                                                                                                               
            request.prepare()                                                                                                                                           
    
            message = request.path_url + hashlib.sha256(str(nonce) + request.body).digest()                                                                             
            hmac_key = base64.b64decode(self.secret_key)                                                                                                                
            signature = hmac.new(hmac_key, message, hashlib.sha512).digest()                                                                                            
            signature = base64.b64encode(signature)                                                                                                                     
    
            request.headers.update({                                                                                                                                    
                'API-Key': self.api_key,                                                                                                                                
                'API-Sign': signature                                                                                                                                   
            })                                                                                                                                                          
            return request                                         
    

    我调用它们(从另一个对象的包装器方法)如下:

    def _request(self, method, url, **kwargs):
        if not self._auth:
            self._auth = KrakenAuth(key, secret)
        if 'auth' not in kwargs:
            kwargs['auth'] = self._auth
        return self._session.request(method, URL + url, **kwargs)                                                                                             
    

    ……但它不起作用。那人评论道 print() PreparedRequest 对象不是 Request 对象,从而调用 request.prepare() 打电话给 PreparedRequest.prepare nothing useful 因为没有 request.data 因为它已经被转换成了 body 属性。

    1 回复  |  直到 6 年前
        1
  •  4
  •   Martijn Pieters    4 年前

    您无法访问 data 属性,因为身份验证对象应用于 requests.PreparedRequest() instance 没有 .data .

    Session.request() call request.<method> session.<method> 具体如下:

    不幸的是,在这个过程中,没有任何一点是原始的 数据 其他任何人都可以使用地图,但 PreparedRequest.prepare() PreparedRequest.prepare_body() 在这些方法中,映射是一个局部变量。您不能从身份验证对象访问它。

    你的选择是:

    • 解码 prepare_body() 更新了地图。

    • 不使用身份验证对象,而是使用来自我的答案的其他路径;显式创建准备好的请求并操纵 第一。

    • 玩Python堆栈并从 准备() 两帧向上的方法。我真的不能推荐这条路。

    PreparedRequest.prepare_body() :

    import base64
    import hashlib
    import hmac
    import time
    try:
        # Python 3
        from urllib.parse import parse_qs
    except ImportError:
        # Python 2
        from urlparse import parse_qs
    
    from requests import AuthBase
    
    URL_ENCODED = 'application/x-www-form-urlencoded'
    
    
    class KrakenAuth(AuthBase):
        """a requests-module-compatible auth module for kraken.com"""
        def __init__(self, key, secret):
            self.api_key    = key
            self.secret_key = secret
    
        def __call__(self, request):
            ctheader = request.headers.get('Content-Type')
            assert (
                request.method == 'POST' and (
                    ctheader == URL_ENCODED or
                    requests.headers.get('Content-Length') == '0'
                )
            ), "Must be a POST request using form data, or empty"
    
            # insert the nonce in the encoded body
            data = parse_qs(request.body)
            data['nonce'] = nonce
            request.prepare_body(data, None, None)
    
            body = request.body
            if not isinstance(body, bytes):   # Python 3
                body = body.encode('latin1')  # standard encoding for HTTP
    
            message = request.path_url + hashlib.sha256(b'%s%s' % (nonce, body)).digest()
            hmac_key = base64.b64decode(self.secret_key)
            signature = hmac.new(hmac_key, message, hashlib.sha512).digest()
            signature = base64.b64encode(signature)
    
            request.headers.update({
                'API-Key': self.api_key,
                'API-Sign': signature
            })
            return request