权限系统是每一个后台系统的「安全地基」:谁(用户)能对什么(资源)做什么(操作),看起来简单,但一旦用户上千万、角色上万、资源量上亿,就要在「功能权限(菜单/按钮)」与「数据权限(行级)」、在「灵活的策略表达」与「毫秒级鉴权性能」之间做系统设计。本文按照系统设计面试的标准答题结构,设计一个融合 RBAC 与 ABAC 的大规模权限系统。
一句话:权限系统的核心是「模型 + 性能」——RBAC 用角色表达稳定的功能权限,ABAC 用属性策略表达灵活的数据权限,缓存与布隆过滤器让亿级数据的鉴权仍然毫秒级完成。
一、需求澄清与量级估算
1.1 需求澄清
面试官给出题目「设计一个权限系统」后,先通过提问明确边界:
- 权限模型:RBAC(角色-权限)、ABAC(属性-策略)、还是二者融合?
- 权限粒度:功能权限(菜单/按钮/接口)、数据权限(行级/字段级)都要吗?
- 用户规模:多少用户、多少角色、多少租户?
- 动态性:权限变更(授权/回收/角色调整)需要多快生效?
- 多租户:不同租户是否隔离权限数据?超管是否跨租户?
- 合规:敏感操作是否需要审计留痕?
明确假设(面向面试的合理假设):
| 需求项 | 假设 |
|---|---|
| 权限模型 | RBAC 为主 + ABAC 扩展数据权限 |
| 权限粒度 | 功能权限(菜单/按钮/接口)+ 行级数据权限 |
| 用户规模 | 1000 万用户、1 万角色、10 万租户 |
| 动态性 | 授权变更 30 秒内生效(准实时) |
| 多租户 | 权限数据按租户隔离,平台超管可跨租户 |
| 审计 | 敏感操作全量留痕 |
1.2 量级估算
| 指标 | 估算值 | 推导 |
|---|---|---|
| 用户数 | 1000 万 | — |
| 角色数 | 1 万 | 各租户自定义角色 |
| 权限点 | 5 万 | 功能权限点(菜单/按钮/API) |
| 用户-角色关系 | 3000 万 | 平均每用户 3 个角色 |
| 鉴权 QPS | 30 万 | 后台/业务接口高频鉴权 |
| 鉴权延迟 | < 10ms | 业务可接受 |
| 数据权限行 | 亿级 | 行级权限涉及资源表 |
一句话:千万用户 × 万级角色 × 亿级数据行的组合,决定了鉴权绝不能实时查表,必须「预计算权限快照 + 多级缓存」走热路径。
二、高层架构设计
┌────────────────────────────┐ ┌────────────────────────────┐
│ 业务系统(调用鉴权) │ │ 管理后台(授权操作) │
│ 功能鉴权 / 数据鉴权 / 审计 │ │ 用户/角色/权限/策略管理 │
└─────────────┬──────────────┘ └─────────────┬──────────────┘
│ 鉴权请求 │ 变更指令
┌─────────────▼─────────────────────────────────▼──────────────┐
│ 鉴权服务(AuthZ) │
│ ┌─────────────┐ ┌─────────────┐ ┌───────────────────────┐ │
│ │ 功能鉴权 │ │ 数据鉴权 │ │ 策略引擎(ABAC) │ │
│ │ (权限快照) │ │ (行级过滤) │ │ (属性匹配/规则求值) │ │
│ └─────────────┘ └─────────────┘ └───────────────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ ┌───────────────────────┐ │
│ │ 用户权限快照 │ │ 数据权限解析 │ │ 审计服务 │ │
│ │ (缓存生成) │ │ (SQL改写) │ │ (敏感操作留痕) │ │
│ └─────────────┘ └─────────────┘ └───────────────────────┘ │
└───────┬───────────────────────┬──────────────────────┬───────┘
│ 预计算 │ 策略数据 │ 审计
┌───────▼───────┐ ┌───────────▼────────┐ ┌──────────▼───────┐
│ Redis 权限缓存 │ │ 权限元数据 MySQL │ │ 审计日志存储 │
│ 快照/布隆/负缓存│ │ 用户/角色/权限/策略 │ │ (Kafka+数仓/ES) │
└───────────────┘ └───────────────────┘ └──────────────────┘
┌─────────────────────────────────────────────┐
│ 变更通知总线(MQ):授权变更 → 缓存失效 → 重算 │
└─────────────────────────────────────────────┘
整体拆为五个模块:
- 接入层:业务系统调用鉴权 SDK/网关,功能鉴权走热路径。
- 鉴权服务:功能鉴权(权限快照)、数据鉴权(行级 SQL 改写)、策略引擎(ABAC)。
- 权限元数据:MySQL 存储用户-角色-权限-策略关系。
- 缓存层:Redis 存权限快照、布隆过滤器与负缓存。
- 变更与审计:MQ 通知缓存失效重算,审计服务全量留痕。
2.1 RBAC + ABAC 融合模型
- RBAC 管「功能权限」:稳定、好理解、可授予——「运营角色能看订单管理菜单」。
- ABAC 管「数据权限」:灵活、属性化——「只能看自己部门的订单」「金额超过 10 万的需风控审批」。
功能权限(RBAC):用户 → 角色 → 权限点(菜单/按钮/API)→ 是否允许
数据权限(ABAC):策略 = 主体属性 + 资源属性 + 环境属性 → 布尔裁决
鉴权请求 = 功能鉴权(快照) AND 数据鉴权(策略)
一句话:RBAC 解决「能不能用这个功能」,ABAC 解决「能用这个功能但只能看哪些数据」——两者合起来才是完整权限,缺一不可。
三、核心组件设计
3.1 权限数据模型
-- RBAC 部分
CREATE TABLE user_role (
user_id BIGINT,
role_id BIGINT,
tenant_id BIGINT, -- 多租户隔离键
PRIMARY KEY (user_id, role_id, tenant_id)
);
CREATE TABLE role (
role_id BIGINT PRIMARY KEY,
tenant_id BIGINT,
name VARCHAR(64),
status TINYINT
);
CREATE TABLE role_perm (
role_id BIGINT,
perm_id BIGINT,
PRIMARY KEY (role_id, perm_id)
);
CREATE TABLE permission (
perm_id BIGINT PRIMARY KEY,
code VARCHAR(64), -- 权限点编码,如 "order:read"
resource VARCHAR(64), -- 资源类型
action VARCHAR(32), -- 操作类型
status TINYINT
);
-- ABAC 部分:策略即规则
CREATE TABLE auth_policy (
policy_id BIGINT PRIMARY KEY,
tenant_id BIGINT,
resource VARCHAR(64), -- 目标资源
effect TINYINT, -- 1允许 0拒绝
condition_json JSON, -- 属性条件(表达式树)
priority INT, -- 优先级
status TINYINT
);
ABAC 策略条件用 JSON 表达式树表达,支持任意属性组合:
{
"resource": "order",
"effect": 1,
"conditions": {
"all": [
{ "subject": "user.department", "op": "==", "value": "resource.order.department" },
{ "environment": "time", "op": "between", "value": ["09:00", "18:00"] }
]
}
}
3.2 功能鉴权:权限快照与多级缓存
功能鉴权是最高频调用,绝不能每次查 MySQL。核心是预计算权限快照:
权限快照:用户在某租户下的「权限点集合」(合并所有角色的权限)
缓存层次:
① 本地缓存(每鉴权节点):毫秒级,存最近 N 用户的快照
② Redis:全局共享,快照 + TTL
③ MySQL:兜底,快照未命中时实时组装
查询路径:
perm-check(user, tenant, permCode)
本地缓存 → Redis → 组装快照(MySQL join)并回填缓存
;; 伪代码:功能鉴权(多级缓存)
(defn check-permission [user tenant perm]
(let [cached (or (local-get user tenant) ; 本地缓存
(redis-get "authz:snap:" user tenant))]
(if cached
(contains? cached perm) ; 集合判定 O(1)
(let [snap (build-snapshot user tenant)] ; 回源组装
(fill-caches user tenant snap)
(contains? snap perm)))))
;; 快照组装(SQL 合并用户所有角色权限)
(defn build-snapshot [user tenant]
(SELECT DISTINCT p.code
FROM user_role ur
JOIN role_perm rp ON ur.role_id = rp.role_id
JOIN permission p ON rp.perm_id = p.perm_id
WHERE ur.user_id = ? AND ur.tenant_id = ?))
3.3 数据权限:行级过滤与 SQL 改写
数据权限要让查询「只返回有权限的行」。两种主流实现:
方案A(SQL 改写 / 注入条件):
业务查询前,鉴权服务解析数据权限策略 → 生成 WHERE 条件
例:订单查询自动追加 " AND department_id = 用户所在部门"
适用:关系型数据库、权限条件可表达为谓词
方案B(后置过滤):
查出结果后逐行用策略过滤
适用:非结构化数据、无法改写 SQL 的场景(但性能差,慎用)
数据权限解析流程:
请求资源(order)+ 操作(read)
→ 加载该资源上对该用户生效的策略(按 priority 排序)
→ 求值条件表达式(属性环境注入)
→ 生成行级过滤谓词 → 注入业务查询 → 返回过滤后结果
要点:行级权限的性能关键在「策略也走缓存」——用户在某资源上的生效策略预计算缓存,SQL 改写才能做到每次查询仅一次策略求值。
3.4 权限变更的实时生效
权限变更(新授权/角色调整)要准实时生效,走「变更通知 + 缓存失效」:
变更链路:
管理后台授权 → 写 MySQL(事务) → 发 MQ 变更事件
→ 鉴权节点消费 → 失效本地缓存 + Redis 快照 → 按需重算
生效时效:秒级~30 秒内
一致性取舍:权限变更允许短暂不一致(30 秒内),换取消引复杂度
;; 伪代码:变更事件处理
(defn on-permission-change [event]
(redis/del (str "authz:snap:" (:user event) ":" (:tenant event)))
(local-evict (:user event) (:tenant event)))
3.5 多租户隔离与超管
隔离粒度:
数据模型:所有权限表带 tenant_id 键
超管:平台级超管(跨租户),通过「超管角色 + tenant=0」表达
越权防护:租户切换校验,防止跨租户授权/读取
审计:
敏感操作(授权/回收/越权尝试)全量落审计日志
审计与权限数据解耦:Kafka → 数仓/ES,支持追溯
四、深入权衡
4.1 缓存粒度:全量快照 vs 增量差集
| 方案 | 内存 | 失效成本 | 适用 |
|---|---|---|---|
| 全量权限点快照 | 中等(每用户几十个点) | 变一次全失效 | 通用场景 |
| 权限点布隆过滤器 | 极小 | 变一次重建 | 权限点极多 |
| 增量差集缓存 | 更省 | 复杂、易错 | 权限点海量 |
结论:默认用「全量快照 + 负缓存」,权限点多到单快照膨胀时再降级为布隆过滤器;负缓存(缓存「无权限」结果)防穿透,是鉴权热路径的关键优化。
4.2 RBAC vs ABAC 的取舍
| 模型 | 优点 | 缺点 | 适用 |
|---|---|---|---|
| RBAC | 直观、好授权、好审计 | 角色爆炸、数据权限难表达 | 功能权限、组织简单 |
| ABAC | 灵活、细粒度、规则化 | 难排查、性能开销、表达复杂 | 数据权限、多条件场景 |
结论:面试与工程实践都应融合——RBAC 做稳定的功能层,ABAC 做灵活的数据层;ABAC 策略不要滥用(策略越多越难维护),能用角色表达的绝不用策略。
4.3 一致性 vs 生效速度
权限变更要求「最终一致 + 准实时」,不是强一致:
允许窗口:授权变更 30 秒内生效(秒级过期)
强一致代价:每次鉴权都要查主库 → 30 万 QPS 直接打崩
一句话:权限是「低频变更、高频查询」的数据,天然适合缓存 + 准实时失效——用 30 秒的一致窗口换取毫秒级鉴权性能,是工程上唯一务实的选择。
五、总结
权限系统设计的关键是「模型融合 + 性能工程」:RBAC 角色模型承载稳定可审计的功能权限,ABAC 属性策略承载灵活的行级数据权限,两者通过统一的鉴权服务对外输出裁决。性能上,功能鉴权走「本地缓存 + Redis 快照 + 负缓存」多级缓存、预计算权限快照;数据鉴权走「策略预加载 + SQL 改写」,让亿级数据行的高频查询仍然毫秒级完成。变更通过 MQ 准实时失效缓存(30 秒窗口),审计全量留痕,多租户靠 tenant_id 隔离。最终,权限系统以「模型清晰、性能达标、变更可控、审计可溯」的姿态,成为业务系统最可靠的安全地基。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。