Pythonurllib2の基本認証の問題


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 See this SO postanswerを使用して確認できます
マークミコフスキー2012年

回答:


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 2015

@TreyStoutに感謝します、私はあなたの提案を含むようにソリューションを編集しました。
yayitswei 2016

ここで同様の問題があります。ロードされた承認済みページのブラウザコンテンツで、キャンセルボタンをクリックすると、パスワードページのコンテンツが表示されます
Mostafa 2018

5

https://stackoverflow.com/a/24048772/1733117からコピーアンドペースト/適応)。

まず、各リクエストに適切なヘッダーが含まれるように、urllib2.BaseHandlerまたはをサブクラス化urllib2.HTTPBasicAuthHandlerして実装できます。http_requestAuthorization

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

2番目のパラメーターは、ドメイン名ではなく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/、それが問題を解決していないようです。
サイモン

ここでは、基本認証を必要とするローカルサーバーに対してこれを試しましたが、add_passwordのURLで正常に機能しました。したがって、私は何か他のものが進行中であることを提案します。
リー

これは、http応答にコード401Unauthorizedヘッダーが含まれている場合にのみ機能し'WWW-Authenticate'ます。このSO投稿の回答を参照してください。
マークミコフスキー2012年

0

現在の解決策は、これを非常にうまく解決するパッケージurllib2_prior_authを使用することです(私は標準ライブラリへの組み込みに取り組んでいます。


Wil itは次のようなURLを開くことができますurllib2.urlopen('http://USER:PASS@example.com/path/')
ddofborg 2016年

これは別の問題です。これが標準で機能しないことを確認しますurllib2か?
mcepl 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.