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을 통해 요청을 보낼 때와 비교하여 Authorization이 전송되지 않은 것을 볼 수 있습니다. 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 post answer를 사용하여 확인할 수 있습니다 .
Mark Mikofski

답변:


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

paypal 인증 (access_token을 받기 위해) 과도 작동합니다. 고마워요, 친구!
DerShodan 2014 년

3
base64.b64encode대신 간단한 호출을 할 수 base64.encodestring있으며 줄 바꿈을 바꿀 필요가 없습니다.
Trey Stout 2015

@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.com, api.foursquare.com, http://api.foursquare.com/v1/,하지만 그 문제를 해결하지 않는 것 같습니다.
Simon

나는 기본 인증이 필요한 로컬 서버에 대해 이것을 시도했으며 add_password의 URL을 사용하여 제대로 작동했습니다. 따라서 나는 다른 무언가가 진행 중이라고 제안합니다.

이것은 http 응답에 코드 401 Unauthorized 헤더 가 포함 된 경우에만 작동합니다 'WWW-Authenticate'. 이 그래서 게시물 답변을 참조하십시오 .
Mark Mikofski

0

나는 현재 솔루션 내 패키지 사용하는 것을 제안 urllib2_prior_auth 에이 꽤 잘 (I 사업 해결 을 포함 표준 lib 디렉토리에 있습니다.


이 같은 열린 URL을 허용 윌urllib2.urlopen('http://USER:PASS@example.com/path/')
ddofborg

이것은 또 다른 문제입니다. 이것이 표준과 작동하지 않는 것이 확실 urllib2합니까?
mcepl
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.