两种权限系统,主要是吐槽设计

July 1, 2020
2020 垃圾活

  过年那段时间真的太难受了。这期间遭受了很多事情。 但是我现在记不住太多东西了。朋友圈里多了很多人,三家公司,三家公司的同事。我不能和以前一样在里面抱怨什么了。赖哥的骚操作积累的太多,我都写不完。

        安全运营平台当时设计权限系统的时候,权限上分了两部分,一个是功能权限,一个是数据权限。功能权限控制是否能访问接口,数据权限控制返回的数据,不具有完全的数据权限的只会返回部分数据。考虑到用户配置的方便性,还有工时等限制,最后采用了参数控制的方式。这样说不是很直观,下面简单说明一下权限表的设计。

id perm_name perm_key perm_args created updated status
1 地址标签库 - 黑地址标签 - 读 black_address_tags_read category=1, 2, 3&status=1 2020-01-01 00:00:00 2020-01-01 00:00:00 1
id role_name created updated status
1 super_user 2020-01-01 00:00:00 2020-01-01 00:00:00 1
id role_id perm_id created updated status
1 1 1 2020-01-01 00:00:00 2020-01-01 00:00:00 1
id user_name created updated
1 caiqingjing 2020-01-01 00:00:00 2020-01-01 00:00:00
id user_id role_id created updated
1 1 1 2020-01-01 00:00:00 2020-01-01 00:00:00
id user_id perm_id created updated
1 1 2 2020-01-01 00:00:00 2020-01-01 00:00:00

应用场景:

        上面6张表里我们可以看出,caiqingjing 是 super_user, super_user 现在有 地址标签库-黑地址标签-读 的权限,虽然 caiqingjing 有读权限,但是只能访问 ‘category in (1, 2) and

status=1’ 条件的数据。

        这个权限系统几乎可以解决所有的权限需求的问题了,但是要达到控制数据权限级别的,不可避免地就要去在接口里面写具体的权限逻辑。另外还有一个问题就是,由于现在配置的权限不是原子化的,配置权限的时候不够灵活。如果要加在考虑之外的权限,权限就会变得多,且乱。所谓的不是原子化,就是 category=1, 2, 3&status=1 如果原子化表示的话,应该是写成四条权限,(black_address_tags_read, category=1), (black_address_tags_read, category=2), (black_address_tags_read, category=3), (black_address_tags_read, status=1) 这样存。

        但是要灵活,就要牺牲掉一些便利性。并且在整理权限的时候也会麻烦很多。当一个用户拥有很多权限的时候,配置权限也就变成了一个麻烦的工作。

        这是两种配置方式,有各自的优缺点。

三哥的优化方案:

       抛弃掉 perm_args, 只保留 key, 也就是说, 将所有的权限拆分成最小权限,原本key=‘black_address_tags_read’, perm_args=‘category=1’。然后被他合并成 key=‘black_address_tags_read:category=1’。写一个自动按照数据库字段生成权限的脚本来实现权限自动化,推广权限系统,让公司其他部门的同事都用上我们这套系统,增加我们团队的核心竞争力。

接下来是我的疯狂输出。不一定对。

        从需求上来看,安全运营平台目前只有两个模块涉及到数据权限。领导的需求也只是想要在某一个模块上加数据权限的需求,这个需求原本是只需要花半天就可以修改完,上线了。三哥为了用上他的新方案,和我们犟了三天。我们仨轮翻上阵,对他设计的新系统提出疑问。

        对于能解决的问题,他采用了极其迂回的道路解决。比如说某一个判断,如果从正向判断,你就需要加 20 条权限,但是如果你反向判断,你只需要加一条权限。但是代价就是你可能无法用统一的代码,需要在程序里面做特殊的处理。可是那又怎么样呢,哪有这么通用的权限系统呢,要是真的有的话,这样的模型早就有人搞出来了。

        对于不能解决的问题,他就开始打太极,装听不懂你在说什么,或这就直接拿"你说的这种情况根本不会出现,你不要过度设计,系统设计的越简单就越稳定,越复杂就越不稳定"类似这样的话来堵你。我举个例子:假如给一个人分配了 admin 和 superuser 的权限,这个人是不是该有 admin 的权限?

       从他设计的权限上来看,假如这个系统现在开设的账号有 20 个,就要在数据库里面存 20 条权限。假如说现在有一个字段 business(业务类型, value=1, 2, 3, 4, 5) 和 一个字段 type(告警类型 value=1, 2, 3, 4, 5),假如说权限需要细分到这俩参数,三哥要我们把权限写成 business 和 type 的全排列,也就是说要写成 perm_name: business=1&type=1 这样的形式,那么就要存 25 条权限。

        我们说你这样配权限的人太麻烦了,他说,那我写个脚本搞成权限生成自动化。你这样生成权限是可以了,可是给什么人分配什么权限,给什么角色分配什么权限,这个怎么自动化脚本做?

        我们说你这样设计不太行啊,不要把数据库的值直接和权限值耦合在一起,万一你这时候又新增一个用户,不就又要新增一条权限,还要新增一条用户权限/角色权限关系。他说,没关系啊,我写个 crontab, 每五分钟跑自动生成权限的脚本不行吗?我问,那就是说,如果新增了一个用户,给他分配了相应的权限,他还要五分钟以后才能看见数据是么?他说,不用五分钟啊,我一分钟跑一次不就完了吗?

        他统一了权限 key 的名字,所有的模块都只有四种 key, 分别是 module_name_read/add/change/delete。比如说现在一个人有这个模块的 change 权限,一个是在审批上,一个是在更新上。审批的话这人是个法务,他可以审批所有申请。一个是更新上,他只能修改自己的申请。那么现在就要给 module_name_change 这个 key 配上权限, 一种是 module_name_change: create_user=self, 一种是 module_name_change: create_user=all (这里只是表达意思,create_user=all 这种, 配成权限的话要看当前系统的用户有几个),上述情况不就冲突了吗?是该用那个呢?

        三哥说,这种情况我们不考虑!如果你非要解决的话就放一个无关的字段到参数里面,比如说普通更新的话就加一个 type 进来,写成 module_name_change: create_user=all&type=1 这样用来区分。

        我们就说,你无关的字段你不应该放到权限里面来,这样时间久了,文档又没有,你还记得当初这个 type 放进来的目的是什么吗?人家不听。

       从推广角度来说,假如说我今天是要准备用他权限的人,我还要去理解他设计的系统,我要熟悉脚本的使用,我还要理解他写的那个三四百行的装饰器。我自己实现一个简单的权限系统也不难,我的成本也不大,我为什么要用他那个贼麻烦,而且也不能做到完全通用的脚本。

        无语了…. 反正就是觉得烦的很。