rest api请求时token的注入

来源:互联网 发布:淘宝分流最多设置多少 编辑:程序博客网 时间:2024/06/06 03:58

申明:本文主要以neutronclient为例进行说明

在请求neutronserver时,需要先创建neutronclient,再通过neutronclient访问neutron server。例如:dash board访问neutron server 时,需要在horirzon/openstack_dashboard/api/neutron.py文件中创建neutronclient.

例如在list router时,函数如下:

def router_list(request, **params):    routers = neutronclient(request).list_routers(**params).get('routers')    return [Router(r) for r in routers]

函数调用neutronclient的list_routers函数。

neutronclient定义如下:

def neutronclient(request):    insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)    cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None)    c = neutron_client.Client(token=request.user.token.id,                              auth_url=base.url_for(request, 'identity'),                              endpoint_url=base.url_for(request, 'network'),                              insecure=insecure, ca_cert=cacert)    return c

from neutronclient.v2_0 import client as neutron_client
因为当前用的是2.0版本,所以在v2_0中创建的neutronclient。创建时带入了token id。

在neutronclient/v2_0/client.py中 client定义如下

class Client(ClientBase):
    def __init__(self, **kwargs):        """Initialize a new client for the Neutron v2.0 API."""        super(Client, self).__init__(**kwargs)        self._register_extensions(self.version)client继承ClientBase,在初始化时首先调用父类的构造函数。
class ClientBase(object):
    def __init__(self, **kwargs):        """Initialize a new client for the Neutron v2.0 API."""        super(ClientBase, self).__init__()        self.retries = kwargs.pop('retries', 0)        self.raise_errors = kwargs.pop('raise_errors', True)        self.httpclient = client.construct_http_client(**kwargs)        self.version = '2.0'        self.format = 'json'        self.action_prefix = "/v%s" % (self.version)        self.retry_interval = 1
在父类的构造函数中,要创建httpclient,然后通过httpclient去访问api.
def construct_http_client(username=None,                          user_id=None,                          tenant_name=None,                          tenant_id=None,                          password=None,                          auth_url=None,                          token=None,                          region_name=None,                          timeout=None,                          endpoint_url=None,                          insecure=False,                          endpoint_type='publicURL',                          log_credentials=None,                          auth_strategy='keystone',                          ca_cert=None,                          service_type='network',                          session=None,                          **kwargs):    if session:        kwargs.setdefault('user_agent', 'python-neutronclient')        kwargs.setdefault('interface', endpoint_type)        return SessionClient(session=session,                             service_type=service_type,                             region_name=region_name,                             **kwargs)    else:        # FIXME(bklei): username and password are now optional. Need        # to test that they were provided in this mode.  Should also        # refactor to use kwargs.        return HTTPClient(username=username,                          password=password,                          tenant_id=tenant_id,                          tenant_name=tenant_name,                          user_id=user_id,                          auth_url=auth_url,                          token=token,                          endpoint_url=endpoint_url,                          insecure=insecure,                          timeout=timeout,                          region_name=region_name,                          endpoint_type=endpoint_type,                          service_type=service_type,                          ca_cert=ca_cert,                          log_credentials=log_credentials,                          auth_strategy=auth_strategy)

construct_http_client函数中,通过是否有session来决定创建SessionClient还是HTTPClient.所以创建的neutronclient的httpclient实际是SessionClient或者HTTPClient。通过日志查看都是SessionClient,大概是因为neutron服务一直在运行中吧,session已经存在。第一次启动是HTTPClient。neutronclient的list_routers函数如下:
def list_routers(self, retrieve_all=True, **_params):    """Fetches a list of all routers for a tenant."""    # Pass filters in "params" argument to do_request    return self.list('routers', self.routers_path, retrieve_all,                     **_params)
list函数如下:
def list(self, collection, path, retrieve_all=True, **params):    if retrieve_all:        res = []        for r in self._pagination(collection, path, **params):            res.extend(r[collection])        return {collection: res}    else:        return self._pagination(collection, path, **params)
_pagination函数如下:
def _pagination(self, collection, path, **params):    if params.get('page_reverse', False):        linkrel = 'previous'    else:        linkrel = 'next'    next = True    while next:        res = self.get(path, params=params)        yield res        next = False        try:            for link in res['%s_links' % collection]:                if link['rel'] == linkrel:                    query_str = urlparse.urlparse(link['href']).query                    params = urlparse.parse_qs(query_str)                    next = True                    break        except KeyError:            break

self.get函数如下:

def get(self, action, body=None, headers=None, params=None):    _logger.info("headers is %s,body is %s"%(headers,body))    return self.retry_request("GET", action, body=body,                              headers=headers, params=params)

retry_request函数如下:

def retry_request(self, method, action, body=None,                  headers=None, params=None):    max_attempts = self.retries + 1    for i in range(max_attempts):        try:            return self.do_request(method, action, body=body,                                   headers=headers, params=params)        except exceptions.ConnectionFailed:            # Exception has already been logged by do_request()            if i < self.retries:                _logger.debug('Retrying connection to Neutron service')                time.sleep(self.retry_interval)            elif self.raise_errors:                raise    if self.retries:        msg = (_("Failed to connect to Neutron server after %d attempts")               % max_attempts)    else:        msg = _("Failed to connect Neutron server")    raise exceptions.ConnectionFailed(reason=msg)

do_request函数如下:

def do_request(self, method, action, body=None, headers=None, params=None):    # Add format and tenant_id    action += ".%s" % self.format    action = self.action_prefix + action    if type(params) is dict and params:        params = utils.safe_encode_dict(params)        action += '?' + urlparse.urlencode(params, doseq=1)    if body:        body = self.serialize(body)    resp, replybody = self.httpclient.do_request(        action, method, body=body,        content_type=self.content_type())    status_code = resp.status_code    if status_code in (requests.codes.ok,                       requests.codes.created,                       requests.codes.accepted,                       requests.codes.no_content):        return self.deserialize(replybody, status_code)    else:        if not replybody:            replybody = resp.reason        self._handle_fault_response(status_code, replybody)

在do_request里,调用了httpclient.do_request函数。在BaseClient中构造了httpclient,do_request函数如下:

def do_request(self, url, method, **kwargs):    # Ensure client always has correct uri - do not guesstimate anything    self.authenticate_and_fetch_endpoint_url()    self._check_uri_length(url)    try:        kwargs.setdefault('headers', {})        if self.auth_token is None:            self.auth_token = ""        kwargs['headers']['X-Auth-Token'] = self.auth_token        resp, body = self._cs_request(self.endpoint_url + url, method,                                      **kwargs)        return resp, body    except exceptions.Unauthorized:        self.authenticate()        kwargs.setdefault('headers', {})        kwargs['headers']['X-Auth-Token'] = self.auth_token        resp, body = self._cs_request(            self.endpoint_url + url, method, **kwargs)        return resp, body

在这个函数中,首先进行token和endpoint_url确认。然后坚持请求url的长度,最大不得超过8192.http默认的请求最大是255,不知道这里怎么是8192.最后将token信息放入header中。然后keystonemiddleware从header中获取token进行验证。


下面说明在第一次请求时没有token,怎么去获取token。

authenticate_and_fetch_endpoint_url函数如下:
def authenticate_and_fetch_endpoint_url(self):    if not self.auth_token:        self.authenticate()    elif not self.endpoint_url:        self.endpoint_url = self._get_endpoint_url()
authenticate函数如下:
def authenticate(self):    if self.auth_strategy == 'keystone':        self._authenticate_keystone()    elif self.auth_strategy == 'noauth':        self._authenticate_noauth()    else:        err_msg = _('Unknown auth strategy: %s') % self.auth_strategy        raise exceptions.Unauthorized(message=err_msg)
_authenticate_keystone函数如下:
def _authenticate_keystone(self):    if self.user_id:        creds = {'userId': self.user_id,                 'password': self.password}    else:        creds = {'username': self.username,                 'password': self.password}    if self.tenant_id:        body = {'auth': {'passwordCredentials': creds,                         'tenantId': self.tenant_id, }, }    else:        body = {'auth': {'passwordCredentials': creds,                         'tenantName': self.tenant_name, }, }    if self.auth_url is None:        raise exceptions.NoAuthURLProvided()    token_url = self.auth_url + "/tokens"    resp, resp_body = self._cs_request(token_url, "POST",                                       body=json.dumps(body),                                       content_type="application/json",                                       allow_redirects=True)    if resp.status_code != 200:        raise exceptions.Unauthorized(message=resp_body)    if resp_body:        try:            resp_body = json.loads(resp_body)        except ValueError:            pass    else:        resp_body = None    self._extract_service_catalog(resp_body)

向keystone获取token。




1 0