Python 3.4中HTTP基本身份验证无法工作

17

我正在尝试使用HTTP基本认证登录REST API,但无法正常工作并显示错误

HTTP error 400: Bad Request

这是我的代码:

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)
5个回答

37
以下Python3代码可以正常运行:
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)

3
最终我被迫做了这件事情。我调用的Nginx服务器在其401响应中没有返回“Www-Authenticate:”头。 我猜测这意味着urllib中的HTTPBasicAuthHandler无法正常工作。 - Dzamo Norton

25

已更新以适用于Python 3.x版本。

Requests库提供了一种更简单的方式来进行此类请求:

import requests

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

在我的测试中,这种方法表现良好。使用urllib.request的解决方案(比如你的方案或者直接从文档中例子复制代码)将无法发送Authentication:头部。


感谢您。为了使用Requests,我下载了zipball并提取后将requests目录复制到我的Python Lib路径下。这是预期的过程吗? - Vab
我遇到了以下错误:<Exception xmlns="http://www.hp.com/PC/REST/API"> <ExceptionMessage>抛出了类型为 'HP.PC.API.Model.Exceptions.InvalidAuthenticationDataException' 的异常。</ExceptionMessage> <ErrorCode>1100</ErrorCode></Exception> - Vab
好的,很高兴听到这个消息。请也接受其中一个答案。 - Anti Veeranna

4
下面的代码来自于https://docs.python.org/3.1/howto/urllib2.html,但是在请求时不会发送认证信息。
# 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)

HTTPPasswordMgrWithDefaultRealm更改为HTTPPasswordMgrWithPriorAuth,并在调用password_mgr.add_password时传递is_authenticated=True对我有效。

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

2
我建议使用larsks推荐的requests库,它让HTTP请求变得更加容易。不过,下面是一个使用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)

另一个细节 - 我尝试了您的代码示例,并收到了“http 401未经授权”的返回值,这将是在验证失败或缺失时预期的响应。

然而,您声称收到了 http 400 错误请求,这使我认为您可能有错误的 URL 或其他问题。


1
在我的情况下,上面的例子非常有用。但是我遇到了以下错误 --- > urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:600)>.. .. 如何解决这个问题? - owgitt

2
urllib.request.HTTPBasicAuthHandler()默认使用HTTPPasswordMgrHTTPPasswordMgr包含一个映射,其中包含领域和top_level_url的密码。
当您执行请求并且服务器返回401时,返回的HTTP标头包含:
Www-Authenticate: Basic realm="a-value"
HTTPPasswordMgr会为返回的领域搜索(user, password),并将使用(user, password)发送新请求。
当你编写以下代码时:
auth_handler = urllib.request.HTTPBasicAuthHandler()
# Never use None to realm parameter.
auth_handler.add_password(None, top_level_url, userName,passWord)

您期望服务器发送None领域(但这是不可能的)。如果您希望您的服务器在Www-Autheader中发送一个空的领域,您应该使用:

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

您可以使用HTTPPasswordMgrWithDefaultRealm代替HTTPPasswordMgr来忽略返回的领域:
auth_handler = urllib.request.HTTPBasicAuthHandler(
    urllib.request.HTTPPasswordMgr()
)
auth_handler.add_password(None, top_level_url, userName,passWord))

如果您的服务器以400响应代码发送给您,那么使用您的示例代码进行身份验证时未被请求。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接