Python, HTTPS GET con autenticazione di base


90

Sto provando a fare un HTTPS GET con autenticazione di base utilizzando python. Sono molto nuovo in Python e le guide sembrano usare diverse librerie per fare le cose. (http.client, httplib e urllib). Qualcuno può mostrarmi come si fa? Come puoi dire alla libreria standard di usare?


2
Vuoi assicurarti che il certificato sia valido?
Andrew Cox

1
Dai un'occhiata a stackoverflow.com/questions/635113/… . Sembra coprire esattamente quello che stai cercando.
Geo

Risposte:


122

In Python 3 funzionerà quanto segue. Sto usando il livello inferiore http.client dalla libreria standard. Controlla anche la sezione 2 di rfc2617 per i dettagli dell'autorizzazione di base. Questo codice non verificherà la validità del certificato, ma imposterà una connessione https. Consulta i documenti http.client su come farlo.

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
La requestdocumentazione del metodo [1] menziona che "le stringhe sono codificate come" ISO-8859-1 ", il set di caratteri predefinito per HTTP". Quindi suggerisco di decodificare con "ISO-8859-1" invece di "ASCII". [1] docs.python.org/3/library/…
jgomo3

22
Per utilizzare le variabili invece di b"username:password"utilizzare: bytes(username + ':' + password, "utf-8").
kenorb

1
@ jgomo3: .decode("ascii")è solo per la conversione bytes-> str. Il risultato di b64encodeè comunque solo ASCII.
Torsten Bronger

1
Mio salvatore. Dopo 4 ore di lotta e un carico di miss-direction.
Conrad B

Come si usano le credenziali predefinite?, Questo non funzionerà se eseguo il codice in un altro sistema giusto?
anandhu

93

Usa la potenza di Python e affidati a una delle migliori librerie in circolazione: le richieste

import requests

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

La variabile r (richieste di risposta) ha molti più parametri che è possibile utilizzare. La cosa migliore è entrare nell'interprete interattivo e giocare con esso e / o leggere i documenti delle richieste .

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

Aggiornamento: OP usa Python 3. Quindi aggiungendo un esempio usando 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")

Quanto segue funziona per Python 2.6:

Io uso pycurl molto in produzione per un processo che fa più di 10 milioni di richieste al giorno.

Dovrai prima importare quanto segue.

import pycurl
import cStringIO
import base64

Parte dell'intestazione di autenticazione di base è costituita dal nome utente e dalla password codificati come Base64.

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

Nell'intestazione HTTP vedrai questa riga Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ= . La stringa codificata cambia a seconda del nome utente e della password.

Ora abbiamo bisogno di un posto in cui scrivere la nostra risposta HTTP e un handle di connessione curl.

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

Possiamo impostare varie opzioni di arricciatura. Per un elenco completo delle opzioni, vedere questo . La documentazione collegata è per l'API libcurl, ma le opzioni non cambiano per le associazioni di altre lingue.

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)

Se non è necessario verificare il certificato. Avvertimento: questo non è sicuro. Simile alla corsa curl -ko curl --insecure.

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

Chiama cStringIO.writeper memorizzare la risposta HTTP.

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

Quando effettui una richiesta POST.

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

Effettua ora la richiesta effettiva.

conn.perform()

Fai qualcosa in base al codice di risposta HTTP.

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

Questo sembra essere per pyhthon 2.5 im usando 3
Tom Squires

Stai usando facile installazione o pip? Il pacchetto pycurl non è disponibile per Python 3?
Ocaj Nires

Aggiornato con un httplib2. Questo è disponibile per Python 3.
Ocaj Nires

Per coloro che sono nuovi: nell'esempio sopra manca un punto: "pycurl.HTTPHEADER" (lo modificherei ma è 1 carattere e il minimo è 6).
Graeme Wicksted

OP ha detto GET, non POST
Joe C

17

Segue un modo corretto per eseguire l'autenticazione di base in Python3 urllib.requestcon la convalida del certificato.

Nota che certifinon è obbligatorio. È possibile utilizzare il bundle del sistema operativo (probabilmente solo * nix) o distribuire il bundle CA di Mozilla da soli. Oppure, se gli host con cui comunichi sono solo pochi, concatena tu stesso il file CA dalle CA degli host, il che può ridurre il rischio di attacchi MitM causati da un'altra CA corrotta.

#!/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())

Questo è fantastico. La verifica del certificato è importante quando si inviano credenziali di testo normale (autenticazione di base HTTP). Devi assicurarti che il tuo livello TLS (HTTPS) sia sicuro perché ti affidi a quel livello per essere sicuro.
four43

Sembra corretto, ma non ha funzionato nel mio caso, genera un errore come ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] verifica certificato non riuscita: impossibile ottenere il certificato dell'emittente locale (_ssl.c: 1056)
neelmeg

L'ho capito passando un certificato pem valido al parametro di verifica e ai parametri dei cookie.
neelmeg

1

utilizzando solo moduli standard e nessuna codifica manuale dell'intestazione

... che sembra essere il modo previsto e più portatile

il concetto di python urllib è raggruppare i numerosi attributi della richiesta in vari gestori / direttori / contesti ... che poi elaborano le loro parti:

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

Basato sulla risposta di @AndrewCox con alcuni piccoli miglioramenti:

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

Nota, dovresti impostare la codifica se usi bytesfunction invece di b"".


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

Se con token, la password dovrebbe essere ''.

Per me funziona.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.