Keystone 用户认证

网友投稿 680 2022-11-07

Keystone 用户认证

Keystone 用户认证

Keystone 用户认证

1、keystone简介

keystone的认证,因为版本原因,同样也分为version 2与version 3,并且version2与3分属不同模块(下述所指模块均基于keystone),version2的代码集中于keystone.token模块,version3的代码集中于keystone.auth模块。

keystone各部件的代码组成主要包括routers、controllers、core、backends(包括sql、kvs等),其中routers主要负责url与action映射,core负责manager api的操作,通俗来讲就是为controller提供api接口,为backends各种后端驱动封装接口,是一种适配器的功能,backends主要提供各种后端存储等操作。

认证的路径URL分别如下:

version2:/tokens对应操作:authenticate/tokens/revoked对应操作:revocation_list/tokens/{token_id} 对应validation_token,validate_token_head,delete_token/tokens/{token_id}/endpoints 对应操作:token_controller/certificates/ca对应操作ca_cert/certificates/signing对应操作signing_certversion3: /auth/tokens对应操作:validate_token,checl_token,authenticate_for_token,revoke_token/auth/tokens/OS-PKI/revoked对应操作:revocation_list/auth/catalog对应操作:get_auth_catalog/auth/domains对应操作:get_auth_domains

上述URL定义以及对应操作的映射分别在keystone.auth.routers和Keystone.token.routers,所映射的操作分别在keystone.auth.controllers和keystone.token.controllers.

其中,auth部件,主要负责各种认证,token部件负责是token的认证,持久化等操作,所以才有了auth与token功能交叉的部分,也就是V2与V3在token认证上的差异.

2、keystone认证

keystone默认配置的认证方法主要有external、password、token、oauth1.

而auth.plugins中目前支持的增加了saml2,也就是liberty版本中的WEBSSO.

下面我们讨论的主要包括password、token、以及external,在对单个认证方式分析时,会同时分析v2与v3的差异。

V3中涉及的概念:project,domain,group,user,trust等,其中domain管理project,group,group管理user,因此,在检测group是否enabled的时候,需要首先检测其所归属集合是否enabled,这个编程涉及思路贯彻始终。

接下来,我们首先介绍version 3的认证流程。

2.1 keystone v3认证

处理函数:authenticate_for_token

def anthenticate_for_token(self, context, auth=None):

# context就是上下环境,auth是输入的字典

# auth的类型有如下几种,这些都是来至官网,对照代码理解无误,请放心!

第一种是username 配合password,因为username不是主键,所以要搭domain,domain可以id或者name方式

{ "auth":{ "identity": { "methods": [ "password" ], "password": { "user": { "domain": { "name": "example.com" }, "name": "Joe", "password": "secretsecret" } } } }}第二种是username 配合password domain id{ "auth": { "identity": { "methods": [ "password" ], "password": { "user": { "domain": { "id": "1789d1" }, "name": "Joe", "password": "secretsecret" } } } }}

第三种是userid配合password

{ "auth": { "identity": { "methods": [ "password" ], "password": { "user": { "id": "0ca8f6", "password": "secretsecret" } } } }}

第四种是token id

{ "auth": { "identity": { "methods": [ "token" ], "token": { "id": "e80b74" } } }}

第五种是获取scoped token 是scoped project

{ "auth": { "identity": { "methods": [ "password" ], "password": { "user": { "id": "0ca8f6", "password": "secretsecret" } } }, "scope": { "project": { "id": "263fd9" } } }}

第六种 获取scoped token 是 scoped domain

{ "auth": { "identity": { "methods": [ "password" ], "password": { "user": { "id": "0ca8f6", "password": "secretsecret" } } }, "scope": { "domain": { "id": "263fd9" } } }}

第七种是获取scoped token是提供project name,因为是name,所以需要提供domain,还是因为不是主键,不好辨别

{ "auth": { "identity": { "methods": [ "password" ], "password": { "user": { "id": "0ca8f6", "password": "secretsecret" } } }, "scope": { "project": { "domain": { "id": "1789d1" }, "name": "project-x" } } }}

第八种是scoped token,提供project name以及domain name

{ "auth": { "identity": { "methods": [ "password" ], "password": { "user": { "id": "0ca8f6", "password": "secretsecret" } } }, "scope": { "project": { "domain": { "name": "example.com" }, "name": "project-x" } } }}

下面我们列出代码,一一道来。我的讲解是一代码加注释的形式进行的,谢谢!

因为认证方法都是通过plug-ins这种形式就是适配,那么是怎么适配的呢?

我们通过前期stevedore.DriverManager加载指定模块,这里的模块来至于/etc/keystone.conf

[auth]选项下methods = external,password,token,oauth1,加载好之后,你在输入auth字典时候,就要自己选哪种认证方式了,后面会讲到,首先先看password,token实现的认证方法,之后看controller函数如何去调用这两种方法.

password认证方法:

def authenticate(self, context, auth_payload, auth_context):user_info = auth_plugins.UserAuthInfo.create(auth_payload, METHOD_NAME)try:self.identity_api.authenticate(context,user_id=user_info.user_id,password=user_info.password)except AssertionError:msg = _('Invalid username or password')raise exception.Unauthorized(msg)auth_context['user_id'] = user_info.user_id

首先调用UserInfo类,这个类它是plug-in的core,其create(auth_payload, METHOD_NAME)主要实现的功能就是先初始化UserInfo实例,然后调用本类的_validate_and_normalize_auth_data(auth_payload)方法,那么这个方法是干嘛的呢?先 查看user字典在不在,不在就报错,

然后获取user字典,user_id,user_name,上面那么多auth格式已经讲到了有user_name必须有domain,domain不管是domain name还是domain id,如果username不为空,那么就要检测domain

看它的enabled字段是否为True,不是就报错,如果user_id不为空,那么获取user_ref,再获取domain_ref,检测domain是否enabled,最终检测user是否enabled,然后将user_ref,user_id,domain_id赋值,这样这个方法就调用完毕,最后最后将’password’赋值给AuthInfo实例的METHOD_NAME属性,这样create静态方法调用完毕.

接下来就是调用identifu_api的authenticate方法,填入参数,关于identity_api的分析,放在后面,如果认证通过,那么最后将user_id填充进auth_context[‘user_id’]字段.

password讲解完毕,接下来就是token的方法.

token的认证方法

def authenticate(self, context, auth_payload, user_context):if 'id' not in auth_payload:raise exception.ValidationError(attribute='id',target='token')token_ref = self._get_token_ref(auth_payload)if token_ref.is_federated_user and self.federation_api:mapped.handle_scoped_token(context, auth_payload, user_context, token_ref,self.federation_api, self.identity_api,self.token_provider_api)else:token_authenticate(context, auth_payload, user_context, token_ref)

token插件实现Token类,首先,校验auth_payload是否存在id,没有就报错,然后调用本类_get_token_ref(auth_payload)方法,这方法做了什么呢?它将auth_payload[‘id’]赋值给token_id,然后调用token_provider_api的validate_token方法,返回token_data,最后返回的是KeystoneToken实例,这个实例继承dict,那么这个KeystoneToken是用来干嘛的?KeystoneToken是V2 token与V3 token都需要使用的中间类,内存里就是存储并使用的它.

token_provider_api.validate_token(self, token_id, belong_to=None)方法完成的功能:首先,调用utils.generate_unique_id(token_id),对token_id进行处理,如果是asn1或者pkiz token,那么就要hash,否则直接返回即可,然后调用_validate_token(unique_id),这个方法会先判断是否需要持久化,如果不要持久化就直接返回self.driver.validate_v3_token(token_id),也就是说直接调用后端的validate_v3_token(token_id)函数,这个函数的功能等下介绍.如果需要持久化的话,那么就调用self._persistence,get_token(token_id)并将返回结果传入self.driver.get_token_version(token_ref)判断token的版本,如果是v2,那么就调用self.driver.validate_v3_token(token_ref)否则调用self.driver.validate_v2_token(token_ref),最后返回token_ref.

因为没有设federated,所以,对后面的判断直接略过,最后调用token_authenticate,这个函数的作用是:先判断是否为oauth_scoped或者trusted_scoped,如果是就报错,不允许它们转变token,如果不是,就继续检测配置项是否支持scoped token转换,如果不允许,就报错,如果允许,就调用wsgi.validate_token_bind,检测token bind类型,默认为permissive,所以,可以放心通过,(这个地方一直不懂audit_ids是用来干嘛的,现在略有点明白了),然后从调用

token_ref.get(‘audit_ids’, [])[-1]获取最后一列,然后就是设置user_context的各种参数.如下所示:

user_context.setdefault('expires_at', token_ref.expires)user_context['audit_id'] = token_audit_iduser_context.setdefault('user_id', token_ref.user_id)user_context['extras'].update(token_ref.get('extras', {}))user_context['method_names'].extend(token_ref.methods)

到目前为止,我们把password与token简单介绍完毕,我们下面看controller类中的方法如果去完成认证过程,代码如下:

include_catalog = 'nocatalog' not in context['query_string']try:auth_info = AuthInfo.create(context, auth=auth)# 调用AuthInfo的create方法,先初始化赋值# self.context = context# self.auth = auth# self._scope_data = (None, None, None, None)# 其中self._scope_data = (domain_id, project_id, trust_ref, unscoped)# 是用于标示auth到底是scoped还是unscoped,又是哪种scoped# 调用_validate_and_normalize_auth(scoped_only)方法# 其用于检测auth是否合规,如果scoped为false,那么就要获取self.auth[‘identity’]# 查看认证方式是否在默认支持的选项中# 其后就是检测是否为scoped# self._scoped_data赋予新检测值auth_context = AuthContext(extras={},method_names=[],bind={})# AuthContext是继承dict的类,用于重构context,防止与认证属性冲突# 它重构了__setitem__方法,对属性进行检测,如果是IDENTITY_ATTRIBUTES属性# 且不与原来值相等,那就赋值不成功,且报错,但是‘expires_at’是可以以最早的时间作为替换.self.authenticate(context, auth_info, auth_context)# 认证在代码后,单独开辟出来讲解if auth_context.get('access_token_id'):auth_info.set_scope(None, auth_context['project_id'], None)# 根据分析,auth_context是没有access_token_id的,所以直接进入下面的方法self._check_and_set_default_scoping(auth_info, auth_context)# 这个方法的目的是检测是否为scoped还是已知为unscoped# 如果都不是,我们就需要设置为scoped project,当然user必须要默认project(domain_id, project_id, trust, unscoped) = auth_info.get_scope()# 这里就是获取scoped tuplemethod_names = auth_info.get_method_names()method_names += auth_context.get('method_names', [])method_names = list(set(method_names))expires_at = auth_context.get('expires_at')metadata_ref = Nonetoken_audit_id = auth_context.get('audit_id')# 这里就是调用token_provider_api处理成为v3 token# 在此过程中如果需要持久化,还要调用方法创建token并存储(token_id, token_data) = self.token_provider_api.issue_v3_token(auth_context['user_id'], method_names, expires_at, project_id,domain_id, auth_context, trust, metadata_ref, include_catalog,parent_audit_id=token_audit_id)if trust:self.trust_api.consume_use(trust['id'])# 这里就是返回头’X--Subject-Token’:token_id了return render_token_data_response(token_id, token_data,created=True)except exception.TrustNotFound as e:raise exception.Unauthorized(e)

其中,我们对上述调用的authenticate进行分析,代码如下:

def authenticate(self, context, auth_info, auth_context):# 如果context环境有remote_user可能就使用的external认证方法# 这个以后讨论,我们主要关心password与token认证if context['environment'].get('REMOTE_USER'):try:external = get_auth_method('external')external.authenticate(context, auth_info, auth_context)except exception.AuthMethodNotSupported:LOG.debug("No 'external' plugin is registered.")except exception.Unauthorized:LOG.debug("Authorization failed for 'external' auth method.")# 设一个 认证方法的字典auth_response = {'methods': []}for method_name in auth_info.get_method_names():# auth_info是AuthInfo实例,调用get_method_names# 即获取self.auth[‘identity’][‘methods’]的值method = get_auth_method(method_name)# 查看代码你会发现,这是获取对应方法加载的实例对象# 加载方式是namespace,通过stevedore.DriverManager进行加载# 有人会问了我没看到调用controllers.load_auth_methods啊?# 因为上面方法的调用是在keystone.server.backends里面进行的调用,# 我们在调试的时候,也需要手动的import这些配置类的,昨晚刚搞明白# 总结一下:我们通过load_auth_methods()加载所有默认提供的方法# 通过get_auth_method获取需要的认证方法resp = method.authenticate(context,auth_info.get_method_data(method_name),auth_context)# 这里就是我们获取的方法了,然后调用plug-ins里面相对应的方法# 认证函数调用后resp应该是None,如果有就说明是有问题的,需要进行其他认证if resp:auth_response['methods'].append(method_name)auth_response[method_name] = resp# 上面就是将认证的信息进行储存if auth_response["methods"]:# 这边就是一个检测过程raise exception.AdditionalAuthRequired(auth_response)if 'user_id' not in auth_context:msg = _('User not found')raise exception.Unauthorized(msg)# 在method.authenticaticate检测通过后,会将user_id填入auth_context中# 如果没有,不就说明是有问题吗?

最后,关于token就是这些,还要就是version 2的token如何认证,数据格式,我们后面会再进行分析.以后关于keystone的文章主要有policy(大体)、token version 2&3(包括格式分析),分层多租户,policy(check细节),以及keystone的整体架构,包括其他部件的分析。还有待提高!

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:CAN总线学习总结
下一篇:限时 机器学习资料(书籍+视频)分享
相关文章

 发表评论

暂时没有评论,来抢沙发吧~