Запити Python: скидання запиту POST на заголовок авторизації


9

Я намагаюся зробити запит API POST за допомогою бібліотеки запитів Python. Я проходжу через 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 lib запитів та Python 2.7.9

Відповіді:


10

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']

У більш нових версіях requests, то Authorizationзаголовок буде відкинутий в додаткових випадках (наприклад , якщо редирект з безпечним для незахищеного протоколу).

Тож, що, можливо, трапляється у вашому випадку, це те, що ваші 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)

Редагувати

Я відкрив запит на запит до бібліотеки запитів на github, щоб додати попередження, коли це станеться. Він чекає, коли буде об'єднано друге схвалення (вже три місяці).


1
Спасибі, це була проблема!
користувач4184113

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.