does urllib2 support preemptive authentication aut

2020-04-16 17:43发布

问题:

I am trying access a REST API.

I can get it working in Curl/REST Client (the UI tool), with preemptive authentication enabled.

But, using urllib2, it doesn't seem to support this by default and I can't find a way to turn it on.

Thanks :)

回答1:

Here's a simple Preemptive HTTP basic auth handler, based on the code from urllib2.HTTPBasicAuthHandler. It can be used in the exact same manner, except an Authorization header will be added to every request with a matching URL. Note that this handler should be used with a HTTPPasswordMgrWithDefaultRealm. That's because there is no realm coming back in a WWW-Authenticate challenge since you're being preemptive.

class PreemptiveBasicAuthHandler(urllib2.BaseHandler):

        def __init__(self, password_mgr=None):
                if password_mgr is None:
                        password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
                self.passwd = password_mgr
                self.add_password = self.passwd.add_password

        def http_request(self,req):
                uri = req.get_full_url()
                user, pw = self.passwd.find_user_password(None,uri)
                #logging.debug('ADDING REQUEST HEADER for uri (%s): %s:%s',uri,user,pw)
                if pw is None: return req

                raw = "%s:%s" % (user, pw)
                auth = 'Basic %s' % base64.b64encode(raw).strip()
                req.add_unredirected_header('Authorization', auth)
                return req


回答2:

similar to @thom-nichols's answer; but subclassing HTTPBasicAuthHandler also handling HTTPS requests.

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

here is an example for dealing with a jenkins server which does not send you 401 http errors (retry with auth). I'm using urllib2.install_opener to make things easy.

jenkins_url = "https://jenkins.example.com"
username = "johndoe"
api_token = "some-cryptic-value"

auth_handler = PreemptiveBasicAuthHandler()
auth_handler.add_password(
    realm=None, # default realm.
    uri=jenkins_url,
    user=username,
    passwd=api_token)
opener = urllib2.build_opener(auth_handler)
urllib2.install_opener(opener)


回答3:

Depending on what kind of authentication is required, you can send the Authorization headers manually by adding them to your request before you send out a body.