web-dev-qa-db-fra.com

Python urllib2 Problème d'authentification de base

Mise à jour: sur la base du commentaire de Lee, j'ai décidé de condenser mon code en un script très simple et de l'exécuter à partir de la ligne de commande:

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)

Malheureusement, il ne générera toujours pas l'en-tête Authorization (selon Wireshark) :(

J'ai un problème pour envoyer AUTH de base sur urllib2. J'ai jeté un œil à cet article et suivi l'exemple. Mon code:

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

Je vois ce qui suit sur le Wire via Wirehark:

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

Vous pouvez voir que l’autorisation n’est pas envoyée, c’est-à-dire quand j’envoie une demande via 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: */*

Pour une raison quelconque, mon code ne semble pas envoyer d'authentification. Quelqu'un voit-il ce qu'il me manque?

merci

-Simon

81
Simon

Le problème pourrait être que les bibliothèques Python, conformément à la norme HTTP, envoient d’abord une demande non authentifiée, puis que si les réponses sont renvoyées avec une nouvelle tentative 401, les informations d’identité correctes sont envoyées. Si les serveurs Foursquare ne faites pas "authentification totalement standard" alors les bibliothèques ne fonctionneront pas.

Essayez d'utiliser des en-têtes pour effectuer l'authentification:

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)

Avait le même problème que vous et a trouvé la solution de ce fil de discussion: http://forums.shopify.com/categories/9/posts/27662

197
yayitswei

Voici ce que j'utilise pour traiter un problème similaire que j'ai rencontré lors de la tentative d'accès à l'API de MailChimp. Cela fait la même chose, juste formaté plus agréable.

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)
5
Hayden Shelton

(copier-coller/adapté de https://stackoverflow.com/a/24048772/1733117 ).

D'abord, vous pouvez sous-classe urllib2.BaseHandler ou urllib2.HTTPBasicAuthHandler, et implémenter http_request afin que chaque demande ait l'en-tête Authorization approprié.

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

Ensuite, si vous êtes paresseux comme moi, installez le gestionnaire globalement

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
dnozay

Le deuxième paramètre doit être un URI et non un nom de domaine. c'est à dire.

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

Je suggérerais que la solution actuelle consiste à utiliser mon paquetage rllib2_prior_auth qui résout ce joli problème (je travaille sur inclusion à la bibliothèque standard.

0
mcepl