web-dev-qa-db-fra.com

L'authentification de base HTTP ne fonctionne pas dans python 3.4

J'essaie de me connecter à une API REST à l'aide de l'authentification de base HTTP mais cela ne fonctionne pas et donne l'erreur

HTTP error 400: Bad Request

Voici mon code:

import urllib.parse
import urllib.request
import urllib.response

# create an authorization handler
#auth_handler = urllib.request.HTTPPasswordMgrWithDefaultRealm()
auth_handler = urllib.request.HTTPBasicAuthHandler()


# Add the username and password.
# If we knew the realm, we could use it instead of None.

userName = "username"
passWord  = "pass"
top_level_url = "http URL"
auth_handler.add_password(None, top_level_url, userName,passWord)


# create "opener" (OpenerDirector instance)
opener = urllib.request.build_opener(auth_handler)



# Install the opener.
# Now all calls to urllib.request.urlopen use our opener.
urllib.request.install_opener(opener)

# use the opener to fetch a URL
try:
    result = opener.open(top_level_url)
    #result = urllib.request.urlopen(top_level_url)
    messages = result.read()
    print (messages)  
except IOError as e:
    print (e)
12
Vab

Le code python3 suivant fonctionnera:

import urllib.request
import base64
req = urllib.request.Request(download_url)

credentials = ('%s:%s' % (username, password))
encoded_credentials = base64.b64encode(credentials.encode('ascii'))
req.add_header('Authorization', 'Basic %s' % encoded_credentials.decode("ascii"))

with urllib.request.urlopen(req) as response, open(out_file_path, 'wb') as 
out_file:
    data = response.read()
    out_file.write(data)
25
Tobias Ernst

Mis à jour pour la compatibilité Python 3.x.

La bibliothèque requêtes offre un moyen beaucoup plus simple de faire ce genre de requête:

import requests

response = requests.get('http://service.example.com',
                        auth=requests.auth.HTTPBasicAuth(
                          'username',
                          'password'))
print(response.text)

Dans mes propres tests, cela fonctionne très bien, tandis qu'une solution impliquant urllib.request (comme le vôtre, ou en utilisant le code textuellement des exemples de la documentation) ne parviendra pas à envoyer le Authentication: entête.

18
larsks

urllib.request.HTTPBasicAuthHandler() utilise par défaut HTTPPasswordMgr. Le HTTPPasswordMgr contient une carte qui a le mot de passe du domaine et le top_level_url.

Lorsque vous effectuez la demande et que le serveur renvoie 401. Les en-têtes HTTP renvoyés contiennent:

Www-Authenticate: Basic realm="a-value"

HTTPPasswordMgr recherche (utilisateur, mot de passe) le domaine renvoyé et une nouvelle demande sera envoyée avec (utilisateur, mot de passe).

Lorsque vous écrivez:

auth_handler = urllib.request.HTTPBasicAuthHandler()
# Never use None to realm parameter.
auth_handler.add_password(None, top_level_url, userName,passWord)

Vous vous attendez à ce que le serveur envoie None royaume (mais ce n'est pas possible). Si vous souhaitez que votre serveur envoie un domaine vide dans Www-Autheader, Tu devrais utiliser

auth_handler.add_password('', top_level_url, userName,passWord)

Vous pouvez utiliser HTTPPasswordMgrWithDefaultRealm au lieu de HTTPPasswordMgr pour ignorer le domaine renvoyé:

auth_handler = urllib.request.HTTPBasicAuthHandler(
    urllib.request.HTTPPasswordMgr()
)
auth_handler.add_password(None, top_level_url, userName,passWord))

Si votre serveur vous envoie un code de réponse 400, avec votre exemple de code, l'authentification n'a pas été demandée.

2
Vincent Maillol

J'utiliserais également la bibliothèque de requêtes comme recommandé par larsks, cela rend les requêtes HTTP beaucoup plus faciles.

Cela dit, voici un exemple de code de travail utilisant urllib

import urllib.parse
import urllib.request
import urllib.response

username = "my_username"
password  = "my_password"
top_level_url = "URL"

# create an authorization handler
p = urllib.request.HTTPPasswordMgrWithDefaultRealm()
p.add_password(None, top_level_url, username, password)

auth_handler = urllib.request.HTTPBasicAuthHandler(p)

opener = urllib.request.build_opener(auth_handler)

urllib.request.install_opener(opener)

try:
    result = opener.open(top_level_url)
    messages = result.read()
    print (messages)
except IOError as e:
    print (e)

Un autre détail - j'ai essayé votre propre exemple de code et je suis revenu "http 401 non autorisé", qui serait la réponse attendue en cas d'échec ou d'authentification manquante.

Cependant, vous prétendez que vous avez reçu une mauvaise requête http 400, ce qui m'amène à penser que vous avez la mauvaise URL ou qu'il y a un autre problème également

2
Anti Veeranna