python - 如何在 Auth 对象的 __call__ 方法中签署 requests.Request 的正文?
问题描述
我正在尝试为 kraken 编写一个不错的身份验证助手。我希望它尽可能自动化,所以它需要:
time.time()*1000
在 POST 正文中添加一个 nonce ( )- 计算 POST 正文上的签名
- 将签名放入标题中
我根据这个答案编写了明显的代码:
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
没有什么用处,因为没有request.data
,因为它已经被转换为一个body
属性。
解决方案
您无法访问请求的属性,data
因为身份验证对象应用于没有属性的requests.PreparedRequest()
实例。.data
Session.request()
调用的正常流程(由所有request.<method>
andsession.<method>
调用使用)如下:
- 使用
Request()
与原始调用相同的所有参数创建实例 - 请求被传递给
Session.prepare_request()
,它首先将会话存储的基值与原始调用的参数合并,然后 - 创建了一个
PreparedRequest()
实例 - 在准备好的请求实例上调用该
PreparedRequest.prepare()
方法Request
,从实例和会话中传入合并的数据。 - 该
prepare()
方法调用各种self.prepare_*
方法,包括PreparedRequest.prepare_auth()
. PreparedRequest.prepare_auth()
调用auth(self)
以使身份验证对象有机会将信息附加到请求中。
对您来说不幸的是,在此流程中,除了 and 之外,其他任何人都无法使用原始映射data
,并且在这些方法中,映射是局部变量。您无法从身份验证对象访问它。PreparedRequest.prepare()
PreparedRequest.prepare_body()
那么你的选择是:
再次解码主体,并使用
prepare_body()
更新的映射进行调用。不使用身份验证对象,而是使用我的答案中的其他路径;明确地创建一个准备好的请求并
data
首先进行操作。使用 Python 堆栈玩得开心,并从
prepare()
两帧以上的方法中提取局部变量。我真的不能推荐这条路。
为了很好地封装身份验证方法,我会使用解码/重新编码;后者通过重用足够简单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
推荐阅读
- excel - 加载到表时,如何让 Excel 保留电源查询中的数据类型?
- javascript - 如何在 JavaScript 测验中包含图像?
- pyqt5 - QGraphicsPixmapItem 没有正确旋转
- linux - 无法理解错误:mov rax, qword [rbp-24 - rbx * 8] 上的有效地址无效
- python - 改进搜索回文的脚本
- oracle - Oracle - 将插入/更新/删除/执行授予公共角色是一种糟糕的安全做法吗?
- arrays - 链表与数组:物理内存中哪个更连续?
- ruby - Eclipse 的动态语言工具包是否支持 Ruby 关键字参数?
- django - 将模型实例从一个模型复制到另一个模型
- c++ - C++入门第5版:C++模板类参数推导