HTTP basic authentication not working in python 3.4

依然范特西╮ 提交于 2019-11-29 08:11:10

问题


I am trying to login to a REST API using HTTP Basic Authentication but it is not working and giving the error

HTTP error 400: Bad Request

Here is my 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)

回答1:


The following python3 code will work:

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)



回答2:


Updated for Python 3.x compatibility.

The requests library offers a far easier way of making this sort of request:

import requests

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

In my own testing this works out fine, while a solution involving urllib.request (like yours, or using the code verbatim from the examples in the documentation) will fail to send the Authentication: header.




回答3:


urllib.request.HTTPBasicAuthHandler() by default uses HTTPPasswordMgr. The HTTPPasswordMgr contains a map that has the password from realm and the top_level_url.

When you perform the request and the server returns 401. The returned HTTP headers contains:

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

The HTTPPasswordMgr searches (user, password) for the returned realm and a new request will be sent with (user, password).

When you write:

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

You expect that the server sends None realm (but it's not possible). If you want your server to send an empty realm in Www-Autheader, you should use

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

You can use HTTPPasswordMgrWithDefaultRealm instead of HTTPPasswordMgr to ignore the returned realm:

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

If your server sends you a 400 response code, with your sample code, then the authentication was not asked.




回答4:


I would also use requests library as recommended by larsks, it makes HTTP requests so much easier.

That said, here is a working code sample using 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)

Another detail - I tried your own code sample and I got back "http 401 unauthorized", which would be the expected response in case of failed or missing auth.

However you claim that you got http 400 bad request, which leads me to think that you either have the wrong url or there is some other issue as well




回答5:


Below code is from https://docs.python.org/3.1/howto/urllib2.html, but not send auth info when request.

# create a password manager
password_mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()

# Add the username and password.
# If we knew the realm, we could use it instead of None.
top_level_url = "http://example.com/foo/"
password_mgr.add_password(None, top_level_url, username, password)

handler = urllib.request.HTTPBasicAuthHandler(password_mgr)

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

# use the opener to fetch a URL
opener.open(a_url)

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

change the HTTPPasswordMgrWithDefaultRealmtoHTTPPasswordMgrWithPriorAuth and pass is_authenticated=True when call password_mgr.add_password worked for me.

password_mgr = urllib.request.HTTPPasswordMgrWithPriorAuth()
password_mgr.add_password(None, url, username, password, is_authenticated=True)


来源:https://stackoverflow.com/questions/29708708/http-basic-authentication-not-working-in-python-3-4

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!