Python, HTTPS GET з базовою автентифікацією


89

Я намагаюся зробити HTTPS GET з базовою автентифікацією за допомогою python. Я дуже новий для python, і путівники, здається, використовують різні бібліотеки, щоб робити щось. (http.client, httplib та urllib). Хто-небудь може показати мені, як це зроблено? Як ви можете сказати стандартній бібліотеці використовувати?


2
Ви хочете переконатись, що сертифікат дійсний?
Ендрю Кокс,

1
Перевірте stackoverflow.com/questions/635113/… . Здається, це охоплює саме те, що ви шукаєте.
Geo

Відповіді:


120

У Python 3 буде працювати наступне. Я використовую нижчий рівень http.client зі стандартної бібліотеки. Також перегляньте розділ 2 rfc2617, щоб отримати докладні відомості про базове авторизацію. Цей код не перевіряє дійсність сертифіката, але встановить з'єднання https. Перегляньте http.client docs про те, як це зробити.

from http.client import HTTPSConnection
from base64 import b64encode
#This sets up the https connection
c = HTTPSConnection("www.google.com")
#we need to base 64 encode it 
#and then decode it to acsii as python 3 stores it as a byte string
userAndPass = b64encode(b"username:password").decode("ascii")
headers = { 'Authorization' : 'Basic %s' %  userAndPass }
#then connect
c.request('GET', '/', headers=headers)
#get the response back
res = c.getresponse()
# at this point you could check the status etc
# this gets the page text
data = res.read()  

5
У requestдокументації до методу [1] згадується, що "Рядки кодуються як" ISO-8859-1 ", набір символів за замовчуванням для HTTP". Тому я пропоную декодувати "ISO-8859-1" замість "ASCII". [1] docs.python.org/3/library/…
jgomo3

22
Щоб використовувати змінні замість b"username:password", використовуйте: bytes(username + ':' + password, "utf-8").
kenorb

1
@ jgomo3: Це .decode("ascii")лише для перетворення bytes-> str. Результатом b64encodeє лише ASCII.
Торстен Бронгер,

1
Спасителю мій. Через 4 години боротьби та навантаження на промах.
Конрад Б,

Як використовувати облікові дані за замовчуванням ?, це не працює, якщо я запускаю код в іншій системі, чи не так?
анандху

91

Використовуйте силу Python і спирайтеся на одну з найкращих бібліотек навколо: запити

import requests

r = requests.get('https://my.website.com/rest/path', auth=('myusername', 'mybasicpass'))
print(r.text)

Змінна r (відповідь на запити) має набагато більше параметрів, якими ви можете скористатися. Найкраще зайти в інтерактивний перекладач і пограти з ним, та / або прочитати документи запитів .

ubuntu@hostname:/home/ubuntu$ python3
Python 3.4.3 (default, Oct 14 2015, 20:28:29)
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import requests
>>> r = requests.get('https://my.website.com/rest/path', auth=('myusername', 'mybasicpass'))
>>> dir(r)
['__attrs__', '__bool__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__nonzero__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_content', '_content_consumed', 'apparent_encoding', 'close', 'connection', 'content', 'cookies', 'elapsed', 'encoding', 'headers', 'history', 'iter_content', 'iter_lines', 'json', 'links', 'ok', 'raise_for_status', 'raw', 'reason', 'request', 'status_code', 'text', 'url']
>>> r.content
b'{"battery_status":0,"margin_status":0,"timestamp_status":null,"req_status":0}'
>>> r.text
'{"battery_status":0,"margin_status":0,"timestamp_status":null,"req_status":0}'
>>> r.status_code
200
>>> r.headers
CaseInsensitiveDict({'x-powered-by': 'Express', 'content-length': '77', 'date': 'Fri, 20 May 2016 02:06:18 GMT', 'server': 'nginx/1.6.3', 'connection': 'keep-alive', 'content-type': 'application/json; charset=utf-8'})

23

Оновлення: OP використовує Python 3. Отже, додавши приклад за допомогою httplib2

import httplib2

h = httplib2.Http(".cache")

h.add_credentials('name', 'password') # Basic authentication

resp, content = h.request("https://host/path/to/resource", "POST", body="foobar")

Нижче працює для python 2.6:

Я pycurlбагато використовую у виробництві для процесу, який робить понад 10 мільйонів запитів на день.

Спочатку потрібно імпортувати наступне.

import pycurl
import cStringIO
import base64

Частина основного заголовка автентифікації складається з імені користувача та пароля, закодованих як Base64.

headers = { 'Authorization' : 'Basic %s' % base64.b64encode("username:password") }

У заголовку HTTP ви побачите цей рядок Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=. Зашифрований рядок змінюється залежно від вашого імені користувача та пароля.

Тепер нам потрібно місце для написання нашої відповіді HTTP та дескриптор з’єднання curl.

response = cStringIO.StringIO()
conn = pycurl.Curl()

Ми можемо встановити різні варіанти завивки. Повний перелік варіантів див. У цьому . Пов’язана документація стосується API libcurl, але параметри не змінюються для прив’язок інших мов.

conn.setopt(pycurl.VERBOSE, 1)
conn.setopt(pycurlHTTPHEADER, ["%s: %s" % t for t in headers.items()])

conn.setopt(pycurl.URL, "https://host/path/to/resource")
conn.setopt(pycurl.POST, 1)

Якщо вам не потрібно перевіряти сертифікат. Попередження: це небезпечно. Подібно до бігу curl -kабо curl --insecure.

conn.setopt(pycurl.SSL_VERIFYPEER, False)
conn.setopt(pycurl.SSL_VERIFYHOST, False)

Виклик cStringIO.writeдля зберігання відповіді HTTP.

conn.setopt(pycurl.WRITEFUNCTION, response.write)

Коли ви робите запит POST.

post_body = "foobar"
conn.setopt(pycurl.POSTFIELDS, post_body)

Зробіть фактичний запит зараз.

conn.perform()

Виконайте щось на основі коду відповіді HTTP.

http_code = conn.getinfo(pycurl.HTTP_CODE)
if http_code is 200:
   print response.getvalue()

Здається, це стосується pyhthon 2.5 im using 3
Tom Squires

Ви використовуєте просту установку або піп? Чи недоступний пакет pycurl для python 3?
Ocaj Nires

Оновлено за допомогою httplib2. Це доступно для python 3.
Ocaj Nires

Для тих, хто новачок: у наведеному вище прикладі відсутня крапка: "pycurl.HTTPHEADER" (я б відредагував, але це 1 символ і мінімум 6).
Грем Вікстед,

OP сказав GET, а не POST
Joe C

17

Нижче наведено правильний спосіб зробити базову автентифікацію в Python3 urllib.requestіз перевіркою сертифіката.

Зауважте, що certifiце не обов’язково. Ви можете використовувати свій пакет ОС (ймовірно, лише * nix) або поширювати пакет CA Mozilla самостійно. Або, якщо хостів, з якими ви спілкуєтесь, лише декілька, об’єднайте файл СА з центра сертифікації хостів, що може зменшити ризик атаки MitM, спричиненої іншим пошкодженим ЦС.

#!/usr/bin/env python3


import urllib.request
import ssl

import certifi


context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
context.verify_mode = ssl.CERT_REQUIRED
context.load_verify_locations(certifi.where())
httpsHandler = urllib.request.HTTPSHandler(context = context)

manager = urllib.request.HTTPPasswordMgrWithDefaultRealm()
manager.add_password(None, 'https://domain.com/', 'username', 'password')
authHandler = urllib.request.HTTPBasicAuthHandler(manager)

opener = urllib.request.build_opener(httpsHandler, authHandler)

# Used globally for all urllib.request requests.
# If it doesn't fit your design, use opener directly.
urllib.request.install_opener(opener)

response = urllib.request.urlopen('https://domain.com/some/path')
print(response.read())

Це чудово. Перевірка сертифіката є важливою під час надсилання облікових даних у звичайному тексті (HTTP Basic Auth). Тоді вам потрібно переконатися, що ваш рівень TLS (HTTPS) захищений, оскільки ви покладаєтесь на цей рівень, щоб бути безпечним.
four43

Виглядає правильно, але в моєму випадку не спрацювало, він видає помилку, як ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] перевірка сертифіката не вдалася: неможливо отримати сертифікат місцевого емітента (_ssl.c: 1056)
neelmeg

Я зрозумів це, передавши дійсний сертифікат pem до параметра param і cookies.
neelmeg

1

використовуючи лише стандартні модулі та відсутність кодування заголовка вручну

... що здається задуманим і найбільш портативним способом

Концепція python urllib полягає в групуванні численних атрибутів запиту за різними менеджерами / директорами / контекстами ..., які потім обробляють їх частини:

import urllib.request, ssl

# to avoid verifying ssl certificates
httpsHa = urllib.request.HTTPSHandler(context= ssl._create_unverified_context())

# setting up realm+urls+user-password auth
# (top_level_url may be sequence, also the complete url, realm None is default)
top_level_url = 'https://ip:port_or_domain'
# of the std managers, this can send user+passwd in one go,
# not after HTTP req->401 sequence
password_mgr = urllib.request.HTTPPasswordMgrWithPriorAuth()
password_mgr.add_password(None, top_level_url, "user", "password", is_authenticated=True)

handler = urllib.request.HTTPBasicAuthHandler(password_mgr)
# create OpenerDirector
opener = urllib.request.build_opener(handler, httpsHa)

url = top_level_url + '/some_url?some_query...'
response = opener.open(url)

print(response.read())

0

На основі відповіді @AndrewCox з деякими незначними вдосконаленнями:

from http.client import HTTPSConnection
from base64 import b64encode


client = HTTPSConnection("www.google.com")
user = "user_name"
password = "password"
headers = {
    "Authorization": "Basic {}".format(
        b64encode(bytes(f"{user}:{password}", "utf-8")).decode("ascii")
    )
}
client.request('GET', '/', headers=headers)
res = client.getresponse()
data = res.read()

Зауважте, вам слід встановити кодування, якщо ви використовуєте bytesфункцію замість b"".


-1
requests.get(url, auth=requests.auth.HTTPBasicAuth(username=token, password=''))

Якщо з маркером, пароль повинен бути ''.

Це працює для мене.

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