修改日期 | 修改人 | 备注 |
2017-11-02 16:54:32[当前版本] | 李豪山 | 。 |
2017-11-02 16:49:08 | 李豪山 | 。 |
2017-11-02 16:42:04 | 李豪山 | 。 |
2017-11-02 16:41:24 | 李豪山 | CREAT |
权限是根据系统设置的安全规则或者安全策略,用户可以访问而且只能访问自己被授权的资源。例如,对于一个文件系统的权限来说,用户A和B只具有查看和拷贝该文件系统下某些文件的权限,而用户C和D不仅有查看和拷贝文件的权限,也具有修改和删除文件的权限,这些权限的划分和授权需要事先通过专门管理员进行操作。在实际的生产系统中,用户数量是庞大的,权限的划分需要结合具体的业务场景,一旦把控不住粒度,工作也是繁重的,那么如何解决这个问题?
业界专门提出了一套权限模型和方法,RBAC(Role Based Access Control),即基于角色(Role)的访问控制方法。它的核心概念
这样,我们就实现了让用户通过成为适当角色的成员而得到这些角色的权限,最终实现权限控制的目的
权限模型图o结合上述的文件系统例子,我们可以去用RBAC去刻画和描述:
一言以蔽之,基于角色的访问控制方法的访问逻辑表达式为“Who对What(Which)进行How的操作”,它的由内到外的逻辑结构为权限->角色->用户,即一个角色对应绑定多个权限,一个用户对应绑定多个角色.
从图中可以看到,用户A和B赋予了“文件普通用户”角色,即他们拥有了“文件查看”和“文件拷贝”的权限;用户C和D同时赋予了“文件普通用户”和“文件管理员用户”的两个角色,即他们拥有了“文件查看”、“文件拷贝”、“文件修改”和“文件删除”。如果后面,我们觉得“文件拷贝”有文件泄密的安全问题,那么只需要从它从“文件普通用户”角色移除就可以了,上述4个用户自然无法实行对文件拷贝的这个操作了,所以RBAC模型对于权限扩展和收缩非常方便.
阐述完权限系统的基本概念后,我们来讲讲,权限系统在互联网时代的分布式系统中,尤其是微服务架构的体系下,有什么样的挑战?它又必须解决哪些问题,最适合采用什么框架和技术去解决这些问题?
经过不断的业界框架的选型对比,原本想采用Spring Security框架来做我们的权限框架,但是经过研究,它有很多优势,但也有明显的两个不足:框架笨重,权限数据持久化层次结构不好,最重要的是无法做数据库持久化。于是我们决定自研开发,那么技术方案有几个特点:
既然有那么多微服务,那么我们把权限系统也微服务化,通过微服务来控制其他微服务的权限,保证整体系统架构的一致性。
未来买单侠所有的业务微服务都将接入到权限微服务中,做统一控制。权限微服务即为独立的公共服务,作为众多微服务中的一员,它必定将遵循买单侠微服务架构的线路,即业务微服务的权限验证,要走阿里云SLB->Zuul Api Gateway,如果是基于Web的权限验证,还需要套入Ngnix Rest请求代理。如下图所示
权限服务与其它公共服务的关系图我们将采用自定义权限的注解(Annotation),尽可能增加新的代码到业务代码层面,减轻业务线的负担。
基本权限/角色数据我们通过MySql的数据中,权限验证数据,则通过Redis集群缓存。那么意味着,对于足够多的权限验证数据缓存Redis集群后,权限微服务全部崩溃也没关系;反之,当Redis集群崩溃,只要权限微服务运行正常,也不影响权限验证,只是性能会稍差而已。
我们将支持业务服务通过RPC方式进行权限验证,支持其他系统(例如WEB)通过REST方式进行权限验证。对于业务服务的,主要是支持接口加注解进行权限拦截验证,即API权限;对于其他系统,一般主要是体现在界面元素的权限校验(例如Web页面上按钮的Enabled/Disabled,通过权限系统来控制),即界面权限
结合Spring OAuth单点登录,Spring Session等,实现安全体系范畴的权限扩展
接下去,我们具体来阐述,权限微服务的核心技术方案,基于界面的权限控制相对容易,就略过了,主要讲一下基于API权限的实现
对于API权限,我们实现基于注解(Annotation)的扫描入库和拦截,不需要业务服务自行在权限Web界面上录入.
API权限以每个接口或者实现类中的方法作为权限资源,每个权限和微服务名(Service Name)挂钩
我们通过在业务服务的API上添加注解的方式,进行权限定义。基础架构部会提供一个权限组件(Permission Component)Jar给业务服务部门,里面包含了自定义的注解,这样的实现方式,对业务服务的影响非常小,增加权限机制只是在代码层面加几个注解而已。具体使用方式如下
对于一个普通的接口类,我们可以这样定义:
|
对于通过Swagger方式暴露出去的API,我们可以这样定义:
|
|
在上述简短的代码中,我们可以发现有四个自定义的注解,@Group、@Permission、@UserId和@UserType。
当API权限定义好以后,我们在权限组件里面加入扫描权限入库和拦截的算法。采用Spring AutoProxy自动代理的框架来实现我们的扫描算法:
|
通过上述阐述,我们就实现了权限的扫描入库和拦截,可以参照下面的流程图:
角色是一组API权限的汇总,每个角色也将和微服务名挂钩。角色组的作用是为了汇总和管理众多的角色
角色管理需要人工在界面上进行操作,角色管理分为角色组增删改查,以及每个角色组下的角色增删改查
角色组管理页面 角色管理界面权限不能直接和用户绑定,必须通过角色作为中间桥梁进行关联。那么我们要实现
API接入的验证方式
通过远程RPC方式的调用,即通过权限API的方式注入,进行远程调用。
Rest调用的验证方式
通过User ID、User Type、Permission Name(权限名,映射于对应的方法名)、Permission Type(区别是API权限还是界面权限),Service Name(应用名)来判断是否被授权,返回结果是true或者false。
用户服务即整合了Ldap系统的用户和桥接业务用户系统
权限服务接入用户服务后,可以在权限授权页面上选取相应的用户进行权限授权。
由于权限服务属于公共服务,它提供面向买单侠所有业务服务的权限接入,所以承受的性能压力会很大,我们通过运用Redis分布式缓存系统缓存已经验证过的权限。但其中需要注意一个策略,当跟某个用户有关的角色,权限添加删除,或者所属的绑定关系发生变更的时候,需要让缓存中的权限数据失效和删除。\
存在的问题:
未来的规划:
我们会考虑通过多种机制实现服务级别的访问控制:
黑/白IP名单机制。当A服务调用B服务的时候,B服务会实现维护一个黑/白IP列表,表示B服务只允许在某个IP网段的A服务才能有权限调用B服务。
服务间约定SecretKey实现安全访问。当A服务调用B服务的时候,两个服务之间实现约定API访问密钥,此密钥不能轻易泄密。这样就规避了B服务被模拟Rest请求调用(例如通过PostMan调用)。
服务的API签名。当A服务调用B服务的时候,A服务需要获得正确的B服务API的签名,才有权限去调用。。