Python, HTTPS GET con autenticación básica

89

Estoy tratando de hacer un HTTPS GET con autenticación básica usando python. Soy muy nuevo en Python y las guías parecen usar diferentes bibliotecas para hacer cosas. (http.client, httplib y urllib). ¿Alguien puede mostrarme cómo se hace? ¿Cómo puede indicarle a la biblioteca estándar que se utilice?

Tom Squires
fuente
2
¿Quiere asegurarse de que el certificado sea válido?
Andrew Cox
1
Consulte stackoverflow.com/questions/635113/… . Parece cubrir exactamente lo que estás buscando.
Geo

Respuestas:

120

En Python 3 funcionará lo siguiente. Estoy usando el http.client de nivel inferior de la biblioteca estándar. Consulte también la sección 2 de rfc2617 para obtener detalles sobre la autorización básica. Este código no verificará que el certificado sea válido, pero establecerá una conexión https. Consulte los documentos http.client sobre cómo hacerlo.

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
fuente
5
La requestdocumentación del método [1] menciona que "Las cadenas están codificadas como" ISO-8859-1 ", el juego de caracteres predeterminado para HTTP". Así que sugiero decodificar con "ISO-8859-1" en lugar de "ASCII". [1] docs.python.org/3/library/…
jgomo3
22
Para utilizar variables en lugar de b"username:password", usar: bytes(username + ':' + password, "utf-8").
kenorb
1
@ jgomo3: El .decode("ascii")es solo para la bytes-> strconversión. De b64encodetodos modos, el resultado de es solo ASCII.
Torsten Bronger
1
Mi Salvador. Después de 4 horas de lucha y un montón de errores de dirección.
Conrad B
¿Cómo utilizo las credenciales predeterminadas? Esto no funcionará si ejecuto el código en otro sistema, ¿verdad?
anandhu
91

Utilice el poder de Python y apóyese en una de las mejores bibliotecas: solicitudes

import requests

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

La variable r (solicita respuesta) tiene muchos más parámetros que puede usar. Lo mejor es entrar en el intérprete interactivo y jugar con él, y / o leer documentos de solicitudes .

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
fuente
23

Actualización: OP usa Python 3. Así que agregue un ejemplo usando http

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

Lo siguiente funciona para Python 2.6:

yo suelo pycurl mucho en producción para un proceso que hace más de 10 millones de solicitudes por día.

Primero deberá importar lo siguiente.

import pycurl
import cStringIO
import base64

Parte del encabezado de autenticación básica consta del nombre de usuario y la contraseña codificados como Base64.

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

En el encabezado HTTP verá esta línea Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ= . La cadena codificada cambia según su nombre de usuario y contraseña.

Ahora necesitamos un lugar para escribir nuestra respuesta HTTP y un identificador de conexión curl.

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

Podemos configurar varias opciones de rizo. Para obtener una lista completa de opciones, consulte esto . La documentación vinculada es para la API libcurl, pero las opciones no cambian para otros enlaces de idioma.

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)

Si no necesita verificar el certificado. Advertencia: esto es inseguro. Similar a correr curl -ko curl --insecure.

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

Llame cStringIO.writepara almacenar la respuesta HTTP.

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

Cuando realiza una solicitud POST.

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

Realice la solicitud real ahora.

conn.perform()

Haga algo basado en el código de respuesta HTTP.

http_code = conn.getinfo(pycurl.HTTP_CODE)
if http_code is 200:
   print response.getvalue()
Ocaj Nires
fuente
Eso parece ser para Pyhthon 2.5, estoy usando 3
Tom Squires
¿Está utilizando instalación fácil o pip? ¿El paquete pycurl no está disponible para python 3?
Ocaj Nires
Actualizado con un archivo httplib2. Esto está disponible para Python 3.
Ocaj Nires
Para aquellos que son nuevos: al ejemplo anterior le falta un punto: "pycurl.HTTPHEADER" (yo editaría pero es 1 carácter y el mínimo es 6).
Graeme Wicksted
OP dijo GET, no POST
Joe C
17

Una forma correcta de realizar autenticación básica en Python3urllib.request con la validación del certificado.

Tenga en cuenta que certifino es obligatorio. Puede usar su paquete de sistema operativo (probablemente * nix solamente) o distribuir el paquete CA de Mozilla usted mismo. O si los hosts con los que se comunica son solo unos pocos, concatene el archivo CA usted mismo de las CA de los hosts, lo que puede reducir el riesgo de un ataque MitM causado por otra CA corrupta.

#!/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
fuente
Esto es genial. La verificación del certificado es importante al enviar credenciales de texto sin formato (autenticación básica HTTP). Debe asegurarse de que su capa TLS (HTTPS) sea segura porque depende de esa capa para estar seguro.
four43
Parece correcto, pero no funcionó en mi caso, arroja un error como ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] verificación del certificado falló: no se pudo obtener el certificado del emisor local (_ssl.c: 1056)
neelmeg
Lo calculé pasando un certificado pem válido al parámetro de verificación y al parámetro de cookies.
neelmeg
1

utilizando solo módulos estándar y sin codificación de encabezado manual

... que parece ser la forma prevista y más portátil

el concepto de Python urllib es agrupar los numerosos atributos de la solicitud en varios gerentes / directores / contextos ... que luego procesan sus partes:

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
fuente
0

Basado en la respuesta de @AndrewCox con algunas mejoras menores:

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

Tenga en cuenta que debe establecer la codificación si usa bytesfunction en lugar de b"".

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

Si tiene token, la contraseña debe ser '' .

Esto funciona para mi.

yidong li
fuente