四、SDK核心模块概览:用户管理、角色管理、权限管理、校验引擎、缓存层

好了,聊完了RBAC权限改造的整体方案和业务流程,今天咱们正式进入SDK的“五脏六腑”。说白了,一个权限SDK能不能落地,就看这几个核心模块扛不扛得住。我常说,你要处理的是“谁有什么权限”这个简单问题,但背后支撑它的却是一整套工程体系。下面这五个模块,就是我一直在用的核心骨架。

RBAC SDK 核心模块架构图 外部请求 携带 身份令牌解析 校验引擎(核心枢纽) 缓存层 权限管理(核心) 用户管理 / 角色管理 权限策略集 回写/失效 命中/返回 允许 / 拒绝 说明: 校验引擎是核心调度中枢

1. 用户管理:一切权限的出发点

说用户管理之前,我想先问你一个问题:一个用户到底能干嘛?这个问题看似简单,实际非常复杂。用户管理模块在SDK里管的是“我是谁”这件事。它包括用户的登录、登出、身份令牌发放、用户基础信息的同步,以及用户与角色的关联。

我个人习惯把用户管理拆成两层:

  • 认证层(Authentication):判断你是不是合法用户。比如用JWT、Session或者SSO协议。
  • 基础信息层(Profile):存储用户的基本属性,像部门、职级、工号,这些有时会直接影响权限。

我记得有一次给一家电商公司做改造。他们的用户信息存在三个系统里,最后同步时出现了“一个人两个角色”的冲突。后来我踩过坑,才意识到用户管理一定要有一个干净的用户-角色关联表,而且得做全局唯一标识。不然你多个系统对同一个用户定义出不同角色,SDK根本没法判断。

小技巧:在用户管理模块,我建议把用户所在的“部门/组织”也缓存一份。很多业务场景下,“部门负责人”这个角色不是通过手动分配的,而是通过组织树自动继承的。这一点在最初设计时就要考虑到。

2. 角色管理:权限的中间层,关键在于“拆分”

角色管理,说白了就是把一堆权限打个包,起个名字。这种“打包”的过程,就是权限治理的核心。我在很多团队见过一种不好的做法:角色直接跟权限点硬绑定,一个角色数百个权限点,多到没法管理。

我们来整理一下角色管理的几个关键动作:

  1. 角色的增删改查:基础中的基础,没什么好说的。
  2. 角色层级管理:比如“主管”角色可以赋予“员工”角色的一切权限,再加几个管理权限。这里需要注意循环继承的问题。
  3. 角色与用户的关系:一个用户可以有多个角色,一个角色也可以分配给多个用户。这跟权限点是多对多的关系。

我曾经在某银行的SDK升级中,遇到一个低级但致命的错误:角色继承写成了“子角色覆盖父角色”,结果一个主管被分配成“员工”角色后,反而没了管理权。 嗯,这种问题特别好查但又特别容易疏忽。所以,角色继承建议用“累加”而不是“覆盖”,再用一个role_hierarchies表专门存这种关系。

3. 权限管理:最终落地的那个“开关”

权限点,也叫Permission或Action,是你能对某个资源做的具体操作,比如“创建订单”、“删除用户”、“查看报表”。权限管理模块负责把这些原子化的“开关”注册好、管理好。

这个模块我通常会维护两张核心表:

permissions存的是权限点名称、唯一Code、所属模块、描述等。
policy存的是权限校验时的表达式规则,比如“只允许在工作日9点到18点操作”。
重点来了:权限点最好不要超过2000个,否则每次全量加载时很慢。我习惯加一个permission_group做分组缓存,只有被访问到的组才调到内存里来。

你可能会问:“那动态权限呢?比如某个操作只能操作自己创建的订单。” 嗯,这个不是普通权限点能覆盖的,需要交给校验引擎去处理,在规则层面做数据权限判断。

4. 校验引擎:权限判断的“大脑”

校验引擎是整个SDK最忙碌的模块,它每时每刻都在处理“用户有没有权限做这件事”的问题。我个人觉得,校验引擎应该有一个清晰的时序图:

  • 第一步:拿到用户的身份令牌。
  • 第二步:通过令牌去缓存里拉取用户拥有的所有角色和权限列表。
  • 第三步:判断你要请求的资源 + 操作,是否在已有的权限列表里。
  • 第四步:如果在,返回true;如果不在,再去服务端DB查一次看有没有遗漏。
  • 第五步:把判断结果写回缓存,下次直接用。

这里有个坑,我曾经在缓存TTL上吃过亏。缓存设太短,每次请求都穿透;设太长,角色改了用户却半天没权限。 我的建议是:对角色变化配一个独立的Redis Key,用zset存版本号,每次校验时先检查版本号是否有变化,有变化就重拉缓存,否则直接用。

避坑指南:校验引擎的“短路逻辑”一定要谨慎开启。有些团队为了性能,在缓存命中后直接返回,但这么做会导致“用户刚刚被撤销了角色,却还是能操作10分钟”的情况。我做过的最稳妥的办法是:在里嵌一个perm_version字段,每次改角色就递增,校验时判断版本是否过期。

5. 缓存层:性能的保障,但也是问题的温床

没有缓存层,你的校验引擎调一次就要查一次DB,并发一上来直接就炸了。我在做SDK时,缓存层被设计成可插拔的。默认提供Redis和本地内存两种,你也完全可以用自己公司的缓存中间件,只要实现了我们定义的接口。

缓存层主要存什么?我列一下:

  • 用户 -> 角色映射:key是userId,value是角色ID列表。
  • 角色 -> 权限点映射:key是roleId,value是权限点Code列表。
  • 权限策略缓存:key是permissionCode,value是策略表达式。
  • 全局配置数据:比如超时时间、白名单IP等。

这里要特别注意缓存一致性的问题。我当初在一个日均千万请求的项目里,采用“双写 + 延迟双删”的策略:更新DB后,先删一次缓存;过1秒再删一次。虽然麻烦,但保证了最终一致性。如果你的业务要求强一致性,那就别走缓存,直接查DB吧,不过你要做好性能的牺牲。

一个实用建议:在缓存失效时,建议加一个“熔断开关”。如果Redis挂了,SDK会自动切换为本地缓存,并在后台记录报警。等Redis恢复后,再切回去。这样起码不会让整个校验链路直接断掉。

好了,上面这五个模块——用户管理、角色管理、权限管理、校验引擎、缓存层——就是SDK最核心的组成。你可能已经注意到了,它们不是平级的,而是一个流程上的协作关系。理解它们各自做什么、怎么衔接,基本上就掌握了这个SDK的60%。剩下的40%是配置、部署、监控和高可用那些外围工程问题。我们后面会慢慢聊。