Python urllib2基本身份验证问题


81

更新:基于Lee的评论,我决定将我的代码压缩为一个非常简单的脚本,然后从命令行运行它:

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)

不幸的是,它仍然不会生成Authorization标题(每个Wireshark):(

我在通过urllib2发送基本AUTH时遇到问题。我看了这篇文章,并跟随了这个例子。我的代码:

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

我通过Wireshark在Wire上看到以下内容:

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

与通过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: */*

由于某种原因,我的代码似乎未发送身份验证-有人看到我丢失了什么吗?

谢谢

-西蒙


1
我想知道问题是否在于网站没有返回'WWW-Authenticate'标题。您可以使用“try: urllib2.urlopen(req) except urllib2.HTTPError, e: print e.headers 查看此帖子”答案对此进行检查。
Mark Mikofski 2012年

Answers:


199

问题可能在于,根据HTTP标准的Python库首先发送未经身份验证的请求,然后仅在通过401重试应答后,才发送正确的凭据。如果Foursquare服务器不执行“完全标准的身份验证”,则库将不起作用。

尝试使用标头进行身份验证:

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)

与您有相同的问题,并从此线程找到了解决方案:http : //forums.shopify.com/categories/9/posts/27662


HTTP错误505:不支持HTTP版本;(
Daniel Magnusson 2012年

还可以与贝宝认证一起使用(以便接收access_token)。非常感谢,队友!
DerShodan 2014年

3
请注意,您可以简单地调用base64.b64encode而不是,base64.encodestring然后不需要替换换行符。
Trey Stout

感谢@TreyStout,我编辑了解决方案以包括您的建议。
yayitswei

类似的问题在这里..在加载授权页面的浏览器内容中,如果单击取消按钮,我可以看到密码页面的内容
Mostafa

5

(复制粘贴/改编自https://stackoverflow.com/a/24048772/1733117)。

首先,您可以继承urllib2.BaseHandler或的子类urllib2.HTTPBasicAuthHandler,然后实现,http_request以便每个请求都具有适当的Authorization标头。

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

然后,如果您像我一样懒,请在全局安装处理程序

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

这是我用来处理尝试访问MailChimp的API时遇到的类似问题的方法。这做同样的事情,只是格式更好。

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)

4

第二个参数必须是URI,而不是域名。即

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

1
谢谢-我应该提到我想,在许多不同的组合http://api.foursquare.comapi.foursquare.comhttp://api.foursquare.com/v1/,但是这似乎并没有解决问题。
西蒙(Simon)2010年

我只是在这里需要基本身份验证的本地服务器上尝试过此操作,并使用add_password中的URL可以正常工作。因此,我建议还有其他事情。
李李

仅当http响应包含代码401未经授权标头时'WWW-Authenticate',此方法才有效;看到这个帖子的答案
Mark Mikofski 2012年

0

我建议当前的解决方案是使用我的urllib2_prior_auth软件包,它可以很好地解决此问题(我致力于将其包含到标准库中。


威尔允许它打开网址,例如urllib2.urlopen('http://USER:PASS@example.com/path/')
ddofborg '16

这是另一个问题。您确定这与标准不兼容urllib2吗?
mcepl
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.