2010-03-09 22 views
76

Actualización: basado en el comentario de Lee decidí condensar mi código a un guión muy simple y ejecutarlo desde la línea de comandos:Python urllib2 autenticación básica Problema

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) 

Lamentablemente todavía no generará la cabecera Authorization . (por Wireshark) :(

estoy teniendo un problema al enviar AUTH básica sobre urllib2 me tomó un vistazo a this article, y seguido 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() 

que estoy viendo lo siguiente en el alambre a través de Wireshark:

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

Se puede ver que la autorización no se envía, frente al enviar una solicitud a través de rizo: 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: */* 

Para algunos ¿Por qué mi código parece no enviar la autenticación? ¿Alguien ve lo que me estoy perdiendo?

gracias

-Simon

+1

Me pregunto si el problema es que el sitio no está devolviendo un encabezado ''WWW-Authenticate''. Puede verificar esto usando 'try: urllib2.urlopen (req) excepto urllib2.HTTPError, e: print e.headers' [Ver esta respuesta de SO SO] (http: // stackoverflow.com/a/9698319/1020470). –

Respuesta

186

El problema podría ser que las bibliotecas de Python, por HTTP estándar, enviar primero una solicitud no autenticado, y sólo si se respondió con un reintento 401, son las credenciales correctas enviadas. Si los servidores de Foursquare no hacen una "autenticación totalmente estándar", las bibliotecas no funcionarán.

Trate de usar cabeceras 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) 

tenía el mismo problema que tú y encontró la solución de este tema: http://forums.shopify.com/categories/9/posts/27662

+3

Gracias, hombre, salvó totalmente mi día! – Ulf

+0

Error HTTP 505: versión HTTP no admitida; ( –

+0

funciona también con autenticación de PayPal (para recibir access_token) Muchas gracias, amigo! – DerShodan

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) 
+0

Gracias - Debería haber mencionado que lo intenté en una serie de combinaciones diferentes 'http: // api.foursquare.com',' api.foursquare.com', 'http: // api.foursquare.com/v1 /' , pero eso no parece resolver el problema. – Simon

+0

Acabo de probar esto contra un servidor local aquí que requiere autenticación básica y con la URL en la contraseña de adición que funcionó bien. Por lo tanto, sugeriría que algo más está en marcha. – Lee

+0

Esto solo funcionará si la respuesta http contiene el código 401 No autorizado ** _ y _ ** el encabezado ''WWW-Authenticate''; ver [esta post respuesta de SO] (http://stackoverflow.com/a/9698319/1020470). –

4

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

Primero puede crear la subclase urllib2.BaseHandler o urllib2.HTTPBasicAuthHandler, e implementar http_request para que cada solicitud tenga el encabezado Authorization 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 usted es perezoso como yo, instale el controlador de nivel mundial

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

sugeriría que la solución actual es utilizar mi paquete urllib2_prior_auth que resuelve esta bastante bien (yo trabajo en inclusion a el estándar lib

+0

Wil permite abrir URL como' urllib2.urlopen ('http: // USER: PASS @ example .com/ruta/') ' – ddofborg

+0

Este es otro problema. ¿Está seguro de que esto no funciona con el estándar' 'urllib2''? – mcepl

+0

No, no funciona :( – ddofborg

2

Esto es lo que estoy usando para tratar 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) 
+0

Genial, esto funciona con python 2.6. 6 – dirceusemighini

Cuestiones relacionadas