Как пройти аутентификацию с помощью модуля запросов, используя хранилище доверия?

В настоящее время я пишу плагин языка python для написанного мной компилятора, который автоматизирует http-вызовы для RESTful API. Мне удалось заставить работать логин/аутентификацию с использованием модулей сокета и ssl, но этот низкоуровневый подход, похоже, создает потенциальные проблемы с анализом ответа для получения токена аутентификации и секрета. Модуль запросов кажется популярным/эффективным, однако я не могу заставить его работать должным образом для моих конкретных потребностей аутентификации. Я использую хранилище доверия в виде файла .pem (содержащего только открытый ключ), который я преобразовал из моего файла .jks, используемого для аутентификации для подключаемого модуля Java. Сервер ожидает, что имя пользователя и пароль будут отправлены в теле запроса в формате json. Вот код, который я пытался использовать:

#Server and login data
...
host = 'localhost'
port = 8443
pem_file = "C:\\Users\\aharasta\\pycert.pem"

#Digest password with MD5 algorithm
m = hashlib.md5()
m.update(password)
encrypted_password = m.hexdigest()

url = <url>
data = {'userid': user_name, 'password': encrypted_password}
json_data = json.dumps(data)
headers = {'Content-type': 'application/json', 'Accept': 'text/plain', 'Content \                     
          Length': len(json_data)} 

r = requests.post(url, headers = headers, data = json_data, cert = pem_file)
print(r)

После выполнения этот код вызовет ошибку SSL с сообщением «Сбой проверки сертификата». Если я добавлю параметр verify = False или verify = pem_file, я получу ответ 404 от сервера. Я также должен отметить, что когда я запускаю сервер в режиме отладки и выполняю запрос (с одним из параметров проверки), он никогда не попадает в методы аутентификации сервера или какие-либо методы в этом отношении. Мы будем очень признательны за любую информацию или помощь в этом вопросе!


person Andrew Harasta    schedule 05.02.2013    source источник


Ответы (1)


Во-первых, есть пара проблем с тем, что вы опубликовали:

  • Вы указываете host и port, но не приводите пример url, поэтому мы можем предположить, что вы используете локальное развертывание для тестирования и действительно можете просматривать запросы к серверу. Я не уверен, о каких методах вы говорите, но если вы используете что-то похожее на Flask для разработки сервера, вы можете не захотеть отправлять аутентификацию в виде данных в кодировке JSON. Есть заголовки аутентификации по какой-то причине, и запросы имеют обработчики аутентификации по какой-то причине. ;-)

  • Вы не должны указывать заголовок Content-Length самостоятельно. запросы сделают это за вас. Кроме того, вы указываете его неправильно (согласно тому, что вы опубликовали), поэтому ошибка 404 может быть связана с получением заголовка, который ваш сервер не распознает.

Теперь не должно быть причин указывать verify=False, и вы можете указать либо cert=pem_file, либо verify=pem_file. Любой из них или оба должны быть в порядке, но вы никогда не должны использовать verify=False.

Наконец, поднятый SSLError говорит вам, что файл pem, который вы предоставляете, не соответствует тому, что указывает сервер. Имея это в виду, вы можете проверить настройки вашего локального сервера. Запросы не обрабатывают саму проверку сертификата, но urllib3 обеспечивает это. Мы просто настроили его на основе предоставленных вами параметров. И я сомневаюсь, что это вина urllib3, поскольку он вызывает SSLError, который возникает из модуля ssl стандартной библиотеки.

Изменить

Объяснение содержится в документации, где указывается *.pem файл с cert недействителен. Вы должны использовать verify='/path/to/file.pem', чтобы сделать это правильно.

Редактировать №2

Чтобы проверить уже отправленный запрос, вы можете сделать это:

r = requests.post(...)
r.request
# PreparedRequest('POST', url, ...)
r.request.body
r.request.headers
# etc.

Чтобы изменить запрос перед отправкой, вы можете сделать следующее:

from requests import Request, Session

s = Session()
r = Request('POST', url, datajson_data, headers=headers)
p = r.prepare()
p.body = 'New body'
p.headers = #etc.
s.send(p, verify=pem_file)
person Ian Stapleton Cordasco    schedule 05.02.2013
comment
Я использую JBoss для развертывания сервера и не имею права изменять код на стороне сервера. Я не понимаю, почему тот же файл pem работает нормально, когда я оборачиваю его в сокет ssl, но не с модулем requests. Я считаю, что единственными заголовками, которые мне нужно указать, являются Content-Type и Accept (возможно, мне не нужно указывать какие-либо заголовки?), поэтому я удалил Content-Length. К сожалению, это не улучшило дела. - person Andrew Harasta; 05.02.2013
comment
Вы должны поставить Content-Type без сомнения, Accept не является обязательным, но это не проблема. Когда вы говорите, что используете сокет ssl, вы имеете в виду вызов import ssl; ss.wrap_socket(socket, certfile=pem_file)? - person Ian Stapleton Cordasco; 05.02.2013
comment
Да, но я использую параметр ca_certs, так что это выглядит так: import ssl; conn = ssl.wrap_socket(sock, ca_certs = pem_file) полную конфигурацию сокета можно найти здесь - person Andrew Harasta; 05.02.2013
comment
Итак, я знаю сервер с собственным самозаверяющим сертификатом. Как ни странно, использование cert='/path/to/pem_file' не сработало, а verify='/path/to/pem_file' сработало (без указания cert). Вы пробовали это? - person Ian Stapleton Cordasco; 05.02.2013
comment
@AndrewHarasta Я отредактировал свой ответ, указав в документации место, где это объясняется. - person Ian Stapleton Cordasco; 05.02.2013
comment
Да, это приводит к ответу статуса <404>. Есть ли способ просмотреть почтовый запрос до его отправки на сервер? Похоже, что REST API, с которым я пытаюсь связаться, не распознает что-то в запросе. Я чувствую, что verify=pem_file должен работать для хранилища доверия, такого как этот. Кроме того, просто хотел поблагодарить вас за то, что вы нашли время помочь мне устранить эту проблему. Я очень впечатлен этим сообществом Stack Overflow! - person Andrew Harasta; 05.02.2013
comment
@AndrewHarasta, что вы можете делать с запросами, нелегко описать в комментарии. Я собираюсь обновить свой ответ, указав, что вы должны сделать, чтобы проверить запрос. Кроме того, я сотрудничаю с запросами, так что это еще одна причина, по которой я лично заинтересован в том, чтобы помочь вам отладить это. :-) - person Ian Stapleton Cordasco; 06.02.2013
comment
s.send(p, verify=pem_file) приводит к ошибке [Errno 10053] Установленное соединение было прервано программным обеспечением на вашем хост-компьютере. Мы используем фреймворк Restlet на стороне сервера. Не уверен, что у вас, ребята, были проблемы с запросами и этой структурой в прошлом. С телом запроса все в порядке, и я просто не вижу, что может вызывать проблемы конкретно в заголовке. str: POST https://localhost:8443/<more path>/login HTTP/1.1 Host: localhost Content-Type: text/json Content-Length: 82 Этот заголовок работает с модулями ssl/socket, тем же файлом .pem и тем же телом запроса. - person Andrew Harasta; 06.02.2013
comment
Я так понимаю, это ConnectionError, который поднят? Я не думаю, что у нас когда-либо были проблемы с этой структурой в прошлом. Кроме того, их документация кажется довольно эпическим троллем. Если вы нажмете на что-либо, кроме Java SE/Java EE/GAE/и т.д. документацию, вас отправляют на машину обратного пути, которая просто перенаправляет без конца. Либо они ненавидят своих пользователей и коллег-разработчиков, либо слишком ленивы, чтобы предоставить хорошую документацию. - person Ian Stapleton Cordasco; 06.02.2013
comment
Да, ConnectionError, за которым следует указанное выше Errno в кавычках. Да, платформа Restlet, вероятно, на данный момент довольно сильно предпочитает Java, и я знаю, что вы имеете в виду об их документации. Это определенно не очень хорошо. Я надеялся, что смогу увидеть весь заголовок запроса, который объединяет запросы, а не просто словарь значений заголовка. Я подозреваю, что это просто строка POST <ulr> HTTP/1.1 вверху, но если это нечто большее, мне интересно, может ли это быть причиной <404>. - person Andrew Harasta; 06.02.2013
comment
Это создано urllib3, я не знаю, как извлечь из него эту информацию. - person Ian Stapleton Cordasco; 08.02.2013