繁体   English   中英

Python 请求:POST 请求丢弃授权 header

[英]Python requests: POST request dropping Authorization header

我正在尝试使用 Python 请求库发出 API POST 请求。 我正在通过Authorization header 但是当我尝试调试时,我可以看到 header 正在被删除。 我不知道是怎么回事。

这是我的代码:

access_token = get_access_token()
bearer_token = base64.b64encode(bytes("'Bearer {}'".format(access_token)), 'utf-8')
headers = {'Content-Type': 'application/json', 'Authorization': bearer_token}
data = '{"FirstName" : "Jane", "LastName" : "Smith"}'
response = requests.post('https://myserver.com/endpoint', headers=headers, data=data)

如上所示,我在请求 arguments 中手动设置了Authorization header,但它缺少实际请求的标头: {'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'User-Agent': 'python-requests/2.4.3 CPython/2.7.9 Linux/4.1.19-v7+'}

另外一条信息是,如果我将POST请求更改为GET请求, Authorization header正常通过!

为什么这个库会放弃 header 用于 POST 请求,我该如何让它工作?

使用请求库的 v2.4.3 和 Python 2.7.9

TLDR

您请求的 url 将 POST 请求重定向到不同的主机,因此请求库会删除Authoriztion标头,以免泄露您的凭据。 要解决此问题,您可以覆盖请求的Session类中的负责方法。

细节

在 requests 2.4.3 中, reqeuests删除Authorization标头的唯一地方是当请求被重定向到不同的主机时。 这是相关代码

 if 'Authorization' in headers: # If we get redirected to a new host, we should strip out any # authentication headers. original_parsed = urlparse(response.request.url) redirect_parsed = urlparse(url) if (original_parsed.hostname != redirect_parsed.hostname): del headers['Authorization']

在较新版本的requests ,在其他情况下将删除Authorization标头(例如,如果重定向是从安全协议到非安全协议)。

因此,在您的情况下可能发生的情况是您的 POST 请求被重定向到不同的主机。 您可以使用 requests 库为重定向主机提供身份验证的唯一方法是通过.netrc文件。 遗憾的是,这只允许您使用 HTTP 基本身份验证,这对您没有多大帮助。 在这种情况下,最好的解决方案可能是将requests.Session子类化并覆盖此行为,如下所示:

from requests import Session

class NoRebuildAuthSession(Session):
    def rebuild_auth(self, prepared_request, response):
        """
        No code here means requests will always preserve the Authorization
        header when redirected.
        Be careful not to leak your credentials to untrusted hosts!
        """

session = NoRebuildAuthSession()
response = session.post('https://myserver.com/endpoint', headers=headers, data=data)

编辑

我已经向 github 上的请求库打开了一个拉取请求,以在发生这种情况时添加警告。 它一直在等待第二次批准合并(已经三个月了)。

这是请求文档所说的:

Authorization headers set with headers= will be overridden if credentials are specified in .netrc, which in turn will be overridden by the auth= parameter. Authorization headers will be removed if you get redirected off-host.

您的请求是否被重定向?

如果是这种情况,请尝试在 post 请求中使用此选项禁用重定向:

allow_redirects=False

我看到的第一个(也许是实际的)问题是您如何创建bearer_token因为您不仅要对令牌进行编码,还要对身份验证类型'Bearer'编码

据我了解,您只需要对令牌进行编码,并且必须在请求标头中提供空白身份验证类型 + 编码的令牌:

bearer_token = str(base64.b64encode(access_token.encode()), "utf8")
headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer {}'.format(bearer_token)}

如果它(也是)重定向问题,您可以简单地找出正确的位置并向此 url 发出请求,或者如果服务器接受此请求,您可能会考虑在POST正文中发送访问令牌。

来自文档: Requests will attempt to get the authentication credentials for the URL's hostname from the user's netrc file. The netrc file overrides raw HTTP authentication headers set with headers=. If credentials for the hostname are found, the request is sent with HTTP Basic Auth. Requests will attempt to get the authentication credentials for the URL's hostname from the user's netrc file. The netrc file overrides raw HTTP authentication headers set with headers=. If credentials for the hostname are found, the request is sent with HTTP Basic Auth.

如果您被重定向,您可以尝试使用allow_redirects=false

循环请求对我有用

    response = do_request(url, access_tok, "GET", payload={}, headers={}, allow_redirect=False)

    if response.status_code in range(300, 310):
        new_response = do_request(response.headers['Location'], access_tok, "GET", payload={}, headers={},)
        # print(new_response.status_code)
        pprint(new_response.json())

您可以尝试在标头中使用自定义授权。

定义自定义身份验证类:

class MyAuth(requests.auth.AuthBase):
def __init__(self, bearer_token):
    self.username = None
    self.bearer_token = bearer_token

def __call__(self, r):
    r.headers['Authorization'] = self.bearer_token
    return r

然后使用它来发送请求:

headers = {'Content-Type': 'application/json'}

data = '{"FirstName" : "Jane", "LastName" : "Smith"}'

response = requests.post('https://myserver.com/endpoint', headers=headers, auth=MyAuth(bearer_token), data=data)

如果这有效,请接受答案。 或者,如果您仍有问题,请告诉我们。 希望这可以帮助。

使用“请求”库在 POST 请求中发送授权标头。 在 Python 中只需使用这个:

requests.post('https://api.github.com/user', auth=('user', 'pass'))

这是一个基本的身份验证。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM