Vấn đề xác thực cơ bản trong Python urllib2


81

Cập nhật: dựa trên bình luận của Lee, tôi quyết định cô đọng mã của mình thành một tập lệnh thực sự đơn giản và chạy nó từ dòng lệnh:

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)

Thật không may, nó vẫn không tạo Authorizationtiêu đề (theo Wireshark) :(

Tôi đang gặp sự cố khi gửi AUTH cơ bản qua urllib2. Tôi đã xem qua bài viết này và làm theo ví dụ. Mã của tôi:

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

Tôi thấy thông tin sau trên Wire qua Wirehark:

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

Bạn có thể thấy Ủy quyền không được gửi, so với khi tôi gửi yêu cầu qua 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: */*

Vì lý do nào đó mà mã của tôi dường như không gửi được xác thực - có ai thấy tôi đang thiếu gì không?

cảm ơn

-simon


1
Tôi tự hỏi nếu vấn đề là trang web không trả lại 'WWW-Authenticate'tiêu đề. Bạn có thể kiểm tra điều này bằng cách sử dụng try: urllib2.urlopen(req) except urllib2.HTTPError, e: print e.headers Xem câu trả lời bài SO này .
Mark Mikofski

Câu trả lời:


199

Vấn đề có thể là các thư viện Python, theo HTTP-Standard, trước tiên gửi một yêu cầu chưa được xác thực và sau đó chỉ khi nó được trả lời bằng cách thử lại 401, thì thông tin đăng nhập chính xác mới được gửi. Nếu các máy chủ Foursquare không thực hiện "xác thực hoàn toàn tiêu chuẩn" thì các thư viện sẽ không hoạt động.

Hãy thử sử dụng tiêu đề để xác thực:

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)

Gặp sự cố giống như bạn và đã tìm thấy giải pháp từ chủ đề này: http://forums.shopify.com/categories/9/posts/27662


Lỗi HTTP 505: Phiên bản HTTP không được hỗ trợ; (
Daniel Magnusson

Hoạt động với xác thực paypal (để nhận access_token). Cảm ơn rất nhiều, bạn đời!
DerShodan

3
Lưu ý rằng bạn có thể gọi đơn giản base64.b64encodethay vì base64.encodestringvà sau đó bạn không cần phải thay thế đường dây mới.
Trey Stout

Cảm ơn @TreyStout, tôi đã chỉnh sửa giải pháp để đưa vào đề xuất của bạn.
yayitswei

Tương tự như vấn đề here..In nội dung trình duyệt của trang có thẩm quyền nạp và nếu tôi bấm nút hủy tôi có thể thấy nội dung trang của mật khẩu
Mostafa

5

(copy-paste / phỏng theo https://stackoverflow.com/a/24048772/1733117 ).

Trước tiên, bạn có thể phân lớp urllib2.BaseHandlerhoặc urllib2.HTTPBasicAuthHandlervà triển khai http_requestđể mỗi yêu cầu có Authorizationtiêu đề thích hợp .

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

Sau đó, nếu bạn lười biếng như tôi, hãy cài đặt trình xử lý trên toàn cầu

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

Đây là những gì tôi đang sử dụng để giải quyết vấn đề tương tự mà tôi gặp phải khi cố gắng truy cập API của MailChimp. Điều này làm điều tương tự, chỉ được định dạng đẹp hơn.

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

Tham số thứ hai phải là URI, không phải tên miền. I E

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

1
Cảm ơn - Tôi có nên nói rằng tôi đã cố gắng trong một số kết hợp khác nhau http://api.foursquare.com, api.foursquare.com, http://api.foursquare.com/v1/, nhưng điều đó dường như không giải quyết vấn đề.
Simon

Tôi vừa thử điều này với một máy chủ cục bộ ở đây yêu cầu xác thực cơ bản và với URL trong add_password, nó hoạt động tốt. Do đó, tôi sẽ gợi ý rằng một cái gì đó khác đang chạy.
Lee

Điều này sẽ chỉ hoạt động nếu phản hồi http chứa mã 401 Không được phép tiêu đề 'WWW-Authenticate'; xem câu trả lời SO bài đăng này .
Mark Mikofski

0

Tôi đề nghị rằng giải pháp hiện tại là sử dụng gói urllib2_prior_auth của tôi giải quyết vấn đề này khá tốt (tôi làm việc trên việc đưa vào lib tiêu chuẩn.


Wil nó cho phép mở các url nhưurllib2.urlopen('http://USER:PASS@example.com/path/')
ddofborg

Đây là một vấn đề khác. Bạn có chắc điều này không hoạt động với tiêu chuẩn urllib2?
mcepl
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.