does urllib2 support preemptive authentication aut

2020-04-16 17:20发布

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

3条回答
我命由我不由天
2楼-- · 2020-04-16 17:47

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
查看更多
一纸荒年 Trace。
3楼-- · 2020-04-16 17:52

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)
查看更多
Fickle 薄情
4楼-- · 2020-04-16 18:11

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.

查看更多
登录 后发表回答