Python, HTTPS GET z podstawowym uwierzytelnianiem

89

Próbuję wykonać HTTPS GET z podstawowym uwierzytelnianiem przy użyciu Pythona. Jestem bardzo nowy w Pythonie, a przewodniki wydają się używać różnych bibliotek do robienia różnych rzeczy. (http.client, httplib i urllib). Czy ktoś może mi pokazać, jak to się robi? Jak możesz powiedzieć bibliotece standardowej, aby używała?

Tom Squires
źródło
2
Chcesz mieć pewność, że certyfikat jest ważny?
Andrew Cox,
1
Sprawdź stackoverflow.com/questions/635113/… . Wydaje się, że obejmuje dokładnie to, czego szukasz.
Geo,

Odpowiedzi:

120

W Pythonie 3 będą działać następujące rzeczy. Używam http.client niższego poziomu z biblioteki standardowej. Sprawdź również sekcję 2 rfc2617, aby uzyskać szczegółowe informacje na temat podstawowej autoryzacji. Ten kod nie sprawdzi, czy certyfikat jest ważny, ale ustanowi połączenie https. Zobacz dokumentację http.client, aby dowiedzieć się, jak to zrobić.

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()  
Andrew Cox
źródło
5
Dokumentacja requestmetody [1] wspomina, że ​​„Ciągi znaków są kodowane jako„ ISO-8859-1 ”, domyślny zestaw znaków HTTP”. Dlatego sugeruję dekodowanie za pomocą „ISO-8859-1” zamiast „ASCII”. [1] docs.python.org/3/library/ ...
jgomo3
22
Używać zmiennych zamiast b"username:password"używać: bytes(username + ':' + password, "utf-8").
kenorb
1
@ jgomo3: .decode("ascii")dotyczy tylko konwersji bytes-> str. Wynik i tak b64encodejest tylko ASCII.
Torsten Bronger
1
Mój Zbawiciel. Po 4 godzinach zmagań i dużej ilości chybionych kierunków.
Conrad B
Jak używać domyślnych poświadczeń? To nie zadziała, jeśli uruchomię kod w innym systemie, prawda?
anandhu
91

Skorzystaj z możliwości Pythona i oprzyj się na jednej z najlepszych bibliotek w zakresie: request

import requests

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

Zmienna r (żąda odpowiedzi) ma dużo więcej parametrów, których możesz użyć. Najlepiej jest wejść do interaktywnego interpretera i pobawić się nim i / lub przeczytać dokumenty z żądaniami .

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'})
IvanD
źródło
23

Aktualizacja: OP używa Pythona 3. Dodam więc przykład używając 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")

Poniższe działa dla Pythona 2.6:

Używam pycurldużo w produkcji w procesie, który wykonuje ponad 10 milionów żądań dziennie.

Najpierw musisz zaimportować następujące elementy.

import pycurl
import cStringIO
import base64

Część podstawowego nagłówka uwierzytelniania składa się z nazwy użytkownika i hasła zakodowanych jako Base64.

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

W nagłówku HTTP zobaczysz tę linię Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=. Zakodowany ciąg zmienia się w zależności od nazwy użytkownika i hasła.

Potrzebujemy teraz miejsca do zapisania naszej odpowiedzi HTTP i uchwytu połączenia curl.

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

Możemy ustawić różne opcje podkręcania. Aby uzyskać pełną listę opcji, zobacz to . Połączona dokumentacja dotyczy interfejsu API libcurl, ale opcje nie zmieniają się w przypadku innych powiązań językowych.

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)

Jeśli nie musisz weryfikować certyfikatu. Ostrzeżenie: to jest niebezpieczne. Podobny do biegania curl -klub curl --insecure.

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

Wezwij cStringIO.writedo przechowywania odpowiedzi HTTP.

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

Kiedy wysyłasz żądanie POST.

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

Złóż rzeczywistą prośbę teraz.

conn.perform()

Zrób coś na podstawie kodu odpowiedzi HTTP.

http_code = conn.getinfo(pycurl.HTTP_CODE)
if http_code is 200:
   print response.getvalue()
Ocaj Nires
źródło
Wygląda na to, że pyhthon 2.5 im używa 3
Tom Squires
Czy używasz łatwej instalacji czy pip? Czy pakiet pycurl jest niedostępny dla Pythona 3?
Ocaj Nires
Zaktualizowano przy pomocy httplib2. Ta opcja jest dostępna dla
języka
Dla tych, którzy są nowi: w powyższym przykładzie brakuje kropki: "pycurl.HTTPHEADER" (edytowałbym, ale jest to 1 znak, a minimum to 6).
Graeme Wicksted
OP powiedział GET, a nie POST
Joe C
17

Poniżej przedstawiono prawidłowy sposób wykonywania podstawowego uwierzytelniania w Python3 urllib.requestz weryfikacją certyfikatu.

Pamiętaj, że certifinie jest to obowiązkowe. Możesz użyć pakietu systemu operacyjnego (prawdopodobnie tylko * nix) lub samodzielnie dystrybuować pakiet CA Mozilli . Jeśli hostów, z którymi się komunikujesz, jest tylko kilka, sam połącz plik CA z urzędów certyfikacji hosta, co może zmniejszyć ryzyko ataku MitM spowodowanego przez inny uszkodzony urząd certyfikacji.

#!/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())
saaj
źródło
To jest świetne. Weryfikacja certyfikatu jest ważna podczas wysyłania poświadczeń w postaci zwykłego tekstu (podstawowe uwierzytelnianie HTTP). Musisz upewnić się, że Twoja warstwa TLS (HTTPS) jest bezpieczna, ponieważ polegasz na tej warstwie, aby była bezpieczna.
four43
Wygląda na poprawny, ale nie działa w moim przypadku, generuje błąd, taki jak ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] weryfikacja certyfikatu nie powiodła się: nie można uzyskać certyfikatu lokalnego wydawcy (_ssl.c: 1056)
neelmeg
Zorientowałem się, że przekazałem ważny certyfikat PEM do parametru weryfikacji i parametru plików cookie.
neelmeg
1

używając tylko standardowych modułów i bez ręcznego kodowania nagłówka

... co wydaje się być zamierzonym i najbardziej przenośnym sposobem

koncepcja python urllib polega na grupowaniu licznych atrybutów żądania w różnych menedżerach / dyrektorach / kontekstach ... które następnie przetwarzają ich części:

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())
Alexey
źródło
0

Na podstawie odpowiedzi @AndrewCox z kilkoma drobnymi ulepszeniami:

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()

Uwaga, powinieneś ustawić kodowanie, jeśli używasz bytesfunction zamiast b"".

I159
źródło
-1
requests.get(url, auth=requests.auth.HTTPBasicAuth(username=token, password=''))

Jeśli z tokenem, hasło powinno być ''.

Mi to pasuje.

yidong li
źródło