Problema de autenticación básica de Python urllib2


81

Actualización: basado en el comentario de Lee, decidí condensar mi código en un script realmente simple y ejecutarlo desde la línea de comando:

import urllib2
import sys

username = sys.argv[1]
password = sys.argv[2]
url = sys.argv[3]
print("calling %s with %s:%s\n" % (url, username, password))

passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
passman.add_password(None, url, username, password)
urllib2.install_opener(urllib2.build_opener(urllib2.HTTPBasicAuthHandler(passman)))

req = urllib2.Request(url)
f = urllib2.urlopen(req)
data = f.read()
print(data)

Desafortunadamente, todavía no generará el Authorizationencabezado (según Wireshark) :(

Tengo un problema para enviar AUTH básica a través de urllib2. Eché un vistazo a este artículo y seguí el ejemplo. Mi código:

passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
passman.add_password(None, "api.foursquare.com", username, password)
urllib2.install_opener(urllib2.build_opener(urllib2.HTTPBasicAuthHandler(passman)))

req = urllib2.Request("http://api.foursquare.com/v1/user")    
f = urllib2.urlopen(req)
data = f.read()

Veo lo siguiente en Wire a través de wirehark:

GET /v1/user HTTP/1.1
Host: api.foursquare.com
Connection: close
Accept-Encoding: gzip
User-Agent: Python-urllib/2.5 

Puede ver que la autorización no se envía, en comparación con cuando envío una solicitud a través de curl: curl -u user:password http://api.foursquare.com/v1/user

GET /v1/user HTTP/1.1
Authorization: Basic =SNIP=
User-Agent: curl/7.19.4 (universal-apple-darwin10.0) libcurl/7.19.4 OpenSSL/0.9.8k zlib/1.2.3
Host: api.foursquare.com
Accept: */*

Por alguna razón, mi código parece no enviar la autenticación. ¿Alguien ve lo que me estoy perdiendo?

Gracias

-Simón


1
Me pregunto si el problema es que el sitio no devuelve un 'WWW-Authenticate'encabezado. Puede verificar esto usando try: urllib2.urlopen(req) except urllib2.HTTPError, e: print e.headers Ver esta respuesta de publicación SO .
Mark Mikofski

Respuestas:


199

El problema podría ser que las bibliotecas de Python, según HTTP-Standard, primero envían una solicitud no autenticada, y luego, solo si se responde con un reintento 401, se envían las credenciales correctas. Si los servidores de Foursquare no realizan una "autenticación totalmente estándar", las bibliotecas no funcionarán.

Intente usar encabezados para realizar la autenticación:

import urllib2, base64

request = urllib2.Request("http://api.foursquare.com/v1/user")
base64string = base64.b64encode('%s:%s' % (username, password))
request.add_header("Authorization", "Basic %s" % base64string)   
result = urllib2.urlopen(request)

Tuve el mismo problema que tú y encontré la solución en este hilo: http://forums.shopify.com/categories/9/posts/27662


Error HTTP 505: Versión HTTP no admitida; (
Daniel Magnusson

También funciona con autenticación de PayPal (para recibir access_token). ¡Muchas gracias, amigo!
DerShodan

3
Tenga en cuenta que puede simplemente llamar en base64.b64encodelugar de base64.encodestringy luego no es necesario reemplazar la nueva línea.
Trey Stout

Gracias @TreyStout, edité la solución para incluir tu sugerencia.
yayitswei

Problema similar aquí ... En el contenido del navegador de la página autorizada cargada y si hago clic en el botón Cancelar puedo ver el contenido de la página de contraseña
Mostafa

5

(copiar y pegar / adaptado de https://stackoverflow.com/a/24048772/1733117 ).

Primero puede crear una subclase urllib2.BaseHandlero urllib2.HTTPBasicAuthHandler, e implementarlo http_requestpara que cada solicitud tenga el Authorizationencabezado apropiado .

import urllib2
import base64

class PreemptiveBasicAuthHandler(urllib2.HTTPBasicAuthHandler):
    '''Preemptive basic auth.

    Instead of waiting for a 403 to then retry with the credentials,
    send the credentials if the url is handled by the password manager.
    Note: please use realm=None when calling add_password.'''
    def http_request(self, req):
        url = req.get_full_url()
        realm = None
        # this is very similar to the code from retry_http_basic_auth()
        # but returns a request object.
        user, pw = self.passwd.find_user_password(realm, url)
        if pw:
            raw = "%s:%s" % (user, pw)
            auth = 'Basic %s' % base64.b64encode(raw).strip()
            req.add_unredirected_header(self.auth_header, auth)
        return req

    https_request = http_request

Entonces, si eres vago como yo, instala el controlador globalmente

api_url = "http://api.foursquare.com/"
api_username = "johndoe"
api_password = "some-cryptic-value"

auth_handler = PreemptiveBasicAuthHandler()
auth_handler.add_password(
    realm=None, # default realm.
    uri=api_url,
    user=api_username,
    passwd=api_password)
opener = urllib2.build_opener(auth_handler)
urllib2.install_opener(opener)

5

Esto es lo que estoy usando para lidiar con un problema similar que encontré al intentar acceder a la API de MailChimp. Esto hace lo mismo, simplemente formateado mejor.

import urllib2
import base64

chimpConfig = {
    "headers" : {
    "Content-Type": "application/json",
    "Authorization": "Basic " + base64.encodestring("hayden:MYSECRETAPIKEY").replace('\n', '')
    },
    "url": 'https://us12.api.mailchimp.com/3.0/'}

#perform authentication
datas = None
request = urllib2.Request(chimpConfig["url"], datas, chimpConfig["headers"])
result = urllib2.urlopen(request)

4

El segundo parámetro debe ser un URI, no un nombre de dominio. es decir

passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
passman.add_password(None, "http://api.foursquare.com/", username, password)

1
Gracias - debería haber mencionado que he intentado en un número de diferentes combinaciones http://api.foursquare.com, api.foursquare.com, http://api.foursquare.com/v1/, pero que no parece resolver el problema.
Simon

Intenté esto en un servidor local aquí que requiere autenticación básica y con la URL en la contraseña add_pass, funcionó bien. Por tanto, sugeriría que hay algo más en marcha.
Lee

Esto solo funcionará si la respuesta http contiene el código 401 No autorizado y el encabezado 'WWW-Authenticate'; ver esta respuesta de la publicación SO .
Mark Mikofski

0

Sugeriría que la solución actual es usar mi paquete urllib2_prior_auth que resuelve esto bastante bien (trabajo en la inclusión en el estándar lib.


Si permite abrir URL comourllib2.urlopen('http://USER:PASS@example.com/path/')
ddofborg

Este es otro problema. ¿Estás seguro de que esto no funciona con el estándar urllib2?
mcepl
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.