Python请求:POST请求删除Authorization标头


9

我正在尝试使用Python请求库发出API POST请求。我正在通过Authorization标头,但是在尝试调试时,可以看到标头已被删除。我不知道发生了什么事。

这是我的代码:

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)

如您在上方所见,我Authorization在请求参数中手动设置了标头,但它缺少实际请求的标头: {'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标头会正常通过!

为什么该库会丢弃POST请求的标头,如何使它正常工作?

使用请求库v2.4.3和Python 2.7.9

Answers:


9

TLDR

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

细节

在请求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']

在的新版本中requestsAuthorization在其他情况下(例如,如果重定向是从安全协议到非安全协议的话),该标头将被丢弃。

因此,在您的情况下可能会发生的情况是将POST请求重定向到其他主机。使用请求库为重定向主机提供身份验证的唯一方法是通过.netrc文件。可悲的是,这仅允许您使用HTTP Basic Auth,这对您没有太大帮助。在这种情况下,最好的解决方案可能是继承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)

1
谢谢,这就是问题所在!
user4184113

0

这是请求文档说的:

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.

您是否在请求中被重定向?

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

allow_redirects=False


allow_redirects=False只会阻止请求遵循服务器请求的重定向。这将无助于完成请求,只会在中间停止它。
kmaork

0

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

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

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

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


0

从文档中: 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


-1

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

定义一个定制认证类:

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)

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


无需从继承requests.auth.AuthBase。如果您查看它的源代码,那么您会发现,NotImplemented如果忘记了重写,它所做的就是提高__call__
恢复莫妮卡

这不会改变问题中描述的行为。在重定向上重建auth时,请求不使用auth参数。
kmaork
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.