史上最强的:京东北极星商业系统权限管控实践
![史上最强的:京东北极星商业系统权限管控实践](https://static001.geekbang.org/infoq/0b/0bfcf717c9136d0a245ad20309089f58.jpeg)
一、ABAC 经典设计案例分析
我们先来说说个经典的设计案例,这个设计案例会让你进一步的理解 ABAC 的能力
其实这个案例在最后的参考文章里,你也可以自己看,我这里是结合着我的理解在讲
AWS IAM 权限策略设计
我们先来看一个 json
// 看不懂没关系,先混个脸熟{ "Action":[ "EditUser", "GetUser" ], "Resource":"user:1、2、3、4", "Condition":{ "DateLessThan":{ "acs:CurrentTime":"2021-03-22T17:00:00+08:00" } }}
我们在上篇文章中说过,权限管控的本质是为了控制一个访问实体可以做什么 ,RBAC 通过角色聚合权限,很容易的做到了这些,但是做过权限系统的人都知道,很多时候还有一些其他条件的影响,以及这个人可以访问什么数据的限制,如果要做到这些,则需要在角色或者权限上面加上各种属性,然后又去根据各种条件过滤,这样子带来的复杂性是呈指数上升的,并且不够通用。我们再看看上面的 json 中,这个 json 我们可以把它视作一个访问策略,在 ABAC 中叫做 policy(上文中已经说过),在这个 policy 中,Action 元素其实就是一系列 api code 的聚合,Resource 元素就是可以访问的资源的集合,condition 元素指的是额外的限定条件,所以这个 json 表示的是,和这个 policy 绑定的人可以在 2021 年的 3 月 22 日之前去对 id 为 1,2,3,4 的四个用户执行这个编辑和查看操作(对应 EditUser 和 GetUser),这样,就做到了增加条件限制以及限定访问的数据。
这个是 AWS 的 IAM 访问策略的设计,也只是其中一个很简单的例子,但也是一个非常经典的设计案例,它的设计思想是源自于 XMACL,XMACL 的文档在结尾的文档中(不推荐学 ABAC 的看这玩意,太复杂了)。
事实上亚马逊的这个 policy 中的元素是非常多的,远远不止这么几个,而且每个元素也是比我例子中的复杂的,比如我下面这个 policy,它是表示获取 AlexaForBusiness 这个资源的所有权限以及和其相关的 AWS 的权限:
// 看不懂也没关系,因为只是为了让你明白思想{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "a4b:*", "kms:DescribeKey" ], "Resource": "*" }, { "Action": [ "iam:CreateServiceLinkedRole" ], "Effect": "Allow", "Resource": "*", "Condition": { "StringLike": { "iam:AWSServiceName": [ "*a4b.amazonaws.com" ] } } }, { "Effect": "Allow", "Action": [ "iam:DeleteServiceLinkedRole", "iam:GetServiceLinkedRoleDeletionStatus" ], "Resource": "arn:aws-cn:iam::*:role/aws-service-role/*a4b.amazonaws.com/AWSServiceRoleForAlexaForBusiness*" }, { "Effect": "Allow", "Action": [ "secretsmanager:GetSecretValue", "secretsmanager:DeleteSecret", "secretsmanager:UpdateSecret" ], "Resource": "arn:aws-cn:secretsmanager:*:*:secret:A4B*" }, { "Effect": "Allow", "Action": "secretsmanager:CreateSecret", "Resource": "*", "Condition": { "StringLike": { "secretsmanager:Name": "A4B*" } } } ]}
再次强调,看不懂没关系,别被这个 json 看晕了,这个策略为啥包含这么多东西,一般是只有设计者才知道的,而我们需要搞懂的是如何去设计。
二、京东北极星商业系统权限系统设计方案
1、ABAC 设计本质
我们从之前的文章中能够明白,ABAC 设计的目的是为了能够满足控制请求者在某些条件下是否对请求数据具备某个操作(API) 的能力,目前来看,ABAC 还暂时没有什么标准建模(policy 不是由实体组成),一般都是 policy 的语法设计,这个指的就是用一个 json 或者一个 xml 来描述一个 policy ,这个 policy 会和用户或者其他实体绑定,然后这个用户或者是实体就具备了在某些条件下对某些数据的操作能力。
为什么 ABAC 没有标准建模呢?作者的经验是认为,ABAC 主要是语法设计,因此会非常灵活,这种非常灵活的关系不适合用模型和模型之间的约束关系来表示,因此目前经典的设计都是用 xml 或者 policy 来表示一个 policy。复制代码
2、业务背景概述及设计难点分析
京东北极星商业系统简单说就是一系列 SaaS 服务的集合,这一系列 SaaS 服务在商业化过程需要有一个统一的底座,底座需要提供权限能力,即北极星商业操作系统需要向一系列的 SaaS 服务去提供权限管控的能力,同时包括控制每个用户渲染的菜单的能力,这要求,我们的权限系统必须足够通用,并且还能够在各种各样复杂的业务场景下进行访问控制(即鉴权服务)。
最后这句话里有个非常难的点就是鉴权,因为我们知道,不同的业务,在鉴权的时候往往会受到业务特性的影响,但是我们的权限系统不可能把所有的这些业务特性都集成进来,一方面这样做权限系统就不是通用的了,再一方面,如果这样做了,那么每有个业务方接入的话,那就必须适配新的业务方的业务逻辑,然后还有就是复杂度会随着时间而呈指数增长,接入方多的话,这样的系统简直会没办法维护。
这时候我们的 ABAC 便派上了用场。
3、ABAC 的设计思路
面对这样的场景,我们该怎么设计 ABAC 呢?我们来看看我们的语法设计:
{ "effect":"Allow", "actions":[ "getUser", "updateUser", "get*" ], "resources":[ "contextField:contextValue:resourceField:resourceValue" ], "condition":[ { "operation-name":{ "condition-key":[ "condition value" ] } } ]}
其中 action 部分还是 api,resource 主要是各种业务条件以及业务数据的约束,condition 部分目前主要是各种时间地理位置的约束,我们可以看出,condition 部分完全可以由权限系统自己来闭环,而 resource 部分是受业务影响的,所以我们重点看看 resource 部分的语法:
为了防止你懵逼,我还是解释一下为什么 condition 部分权限系统自己就可以闭环?这是因为我们权限系统的设计 condition 部分就是一些公共的条件,比如时间,如果是要求当前请求只能是早上八点到晚上的九点才可以访问,那么你完全可以获取请求的当前时间,然后再和 policy 中的 condition value 去做比对,以下是一个约束时间的 json:{ "effect":"Allow", "actions":[ "getOrder" ], "resources":[ "contextField:contextValue:resourceField:resourceValue" ], "condition":[ { "DateLessThan":{ "CurrentTime":[ "2021-03-30 17:00:00" // 必须满足时间小于 2021-03-30 17:00:00 ] } } ]}
然后我们看看 resource 的语法设计,这里我先抛出一个业务场景,现在有个客服 SaaS 系统,采用的是租户模式,每个租户下有很多商家,每个商家又有很多客服人员,这就意味着每个租户有个主商家,系统中有些事情是只有主商家才可以做的。这时候我们的 json 可以怎么设计呢?我们来看看如下例子:
{ "effect":"Allow", "actions":[ "getOrder" ], "resources":[ "isMain:1:orderId:12345" // 并且必须是主商家,且只能访问 orderId 为 123456 的订单 ], "condition":[ { "DateLessThan":{ "CurrentTime":[ "2021-03-30 17:00:00" // 必须满足时间小于 2021-03-30 17:00:00 ] } } ]}
我们重点来看看 resource 中 “isMain:1:orderId:12345” 这个玩意,isMain 就是一个业务字段,含义是是否为主商家,1 的意思为主商家,所以这个 json 的意思就是用户所在商户必须是主商家并且访问时间必须是早于 2021-03-30 17:00:00 才可以获取 id 为 12345 的订单(getOrder 是获取订单),当用户和我们这个策略绑定之后呢,便可以这样去对用户鉴权了。
但是聪明的你肯定注意到了,权限系统怎么知道当前访问者所在商家是否是主商家呢?这是个超级重点的问题,这里有很多权限系统就走入一个坑,这个坑是什么呢?就是它们会去调用业务系统去查,这个做法是大错特错的,我们不能去依赖业务系统去做权限管控的,否则每来一个业务系统对接,我们还得去开发,另外就是复杂度会随着业务系统的变多而呈指数增长,这是无法接受的。那么我们是怎么做的呢?我们观察到,一般这类属性都是很重要的属性,其实在前端页面发起请求之前,就已经查看过商家的信息了,这时候让业务系统前端把这些在 policy 中的业务属性带在请求头中就行,权限系统此时就可以根据这些数据和 policy 来做比对,从而完成鉴权,并且,这样子做,业务系统的改造量是非常小的。
你一定发现了一些通用的设计技巧但又不知道是啥对吗?大方的我再次告诉你,policy 就像是我们和权限系统做的约定,resource 部分就是告诉权限系统我会用这些业务字段来鉴权,然后我请求的时候再把这些值带上,权限系统来计算即可复制代码
4、RBAC 和 ABAC 的优势及劣势
我们在前面文章中说过,RBAC 在应对复杂场景的时候会显得很吃力,但是它有没有好处呢?答案是当然有,它的好处显而易见就是简单且容易理解,我们很容易想象,一般不是开发者的这种用户创建一个角色是多么的容易,但是你如果让他去写一个策略的 json,那他怕是要骂娘,因为一般人根本都不知道 json 是干嘛的,所以就更不可能会写 json。
ABAC 的优点就是 RBAC 的缺点,它可以胜任在复杂场景下的鉴权工作,但是不知道读者们注意到了上面的 json 中没有菜单信息 这一点了没,往具体的说就是如果你的系统使用的是个很纯粹的 ABAC 权限模型,那么,你的系统是不具备管控用户可以渲染的菜单的能力的,这时候肯定有人说,我可以通过一个人的所绑定的策略(ABAC 中 policy 一般都是分配给人的)计算出他能访问的所有 API 然后去渲染 API 所绑定的菜单呀,这个想法其实是大错特错的,一方面,因为 policy 中有的条件是动态的,比如时间,如果纯粹靠计算来的话,那么这一刻和下一刻看到的菜单很有可能是不一样的(因为时间可能也是一种条件),另一方面就是,API 和菜单往往不是一一对应关系,也就是说,API 上面可能绑定了多个菜单,这时候就会有粒度的问题。
5、RBAC 和 ABAC 取舍以及结合
结合上面的业务,我们来分析下我们的需求,我们需要做到的有如下的几个点:
向各种各样的业务去提供一个统一的、满足各类业务要求的鉴权服务
控制业务系统的菜单渲染能力
不能向用户展现太大的复杂度
要让原本的业务系统使用我们的权限系统之后业务改造成本足够低
不能与业务系统产生过多交互
为了同时满足上面的要求,我们结合使用了 RBAC 和 ABAC 的能力,大致建模如下:
![](https://static001.geekbang.org/infoq/34/346f1947cf5ee57a62d43b0b6dad5f45.png)
注意哈,这个不是数据库建模,而是一个 DDD 的建模,具体的我就再不能解释了,再解释就涉及到商业机密了,大家可以参考这个设计,或者添加助手 v【bjmsb10】免费获取更多项目设计
评论