3、权限模型基础:RBAC、ABAC、PBAC 等经典模型介绍

好,聊完了令牌的生命周期和安全约束,我们该聊聊权限模型了。

你想想看,令牌发出去之后,凭什么决定它能不能访问某个接口、读取某条数据?

背后就是一套权限模型在起作用。说白了,权限模型就是一套规则——谁能对什么资源做什么操作。

几十年来,业界沉淀出了几种经典模型。我个人习惯在做令牌服务之前,先把这部分想清楚,不然业务一复杂,权限就乱成一锅粥。

3.1 RBAC——基于角色的权限控制

RBAC 是大家最熟悉的,也是我最早落地的权限模型。核心思路很简单:把权限赋给角色,再把角色赋给用户。

用户和权限不直接关联。中间隔了一层“角色”。

举个例子:

  • 主体(Subject):张三
  • 角色(Role):普通用户
  • 权限(Permission):查看文章、评章

张三是“普通用户”,而“普通用户”拥有“查看文章”和“评章”的权限。如果明天新增一个“发布文章”功能,我只需要改角色,不用改张三。

核心原则:角色是人肉理解的业务概念,权限是机器执行的原子操作。

我在做早期电商平台时,就用的 RBAC。梳理出“买家”、“卖家”、“客服”、“管理员”4个角色,然后每个角色挂上对应的 API 权限。简洁、直观。但后来问题也暴露了——角色越来越多。

我的经验:RBAC 只适合人员变动不频繁、权限变化小的场景。我见过不少团队把 RBAC 玩成了“角色爆炸”,一个业务场景就创建十几个角色,最后谁也看不清谁有什么权限。

优势 劣势
模型简单,易于理解 角色膨胀后难以维护
权限分配效率高 无法表达细粒度条件(如时间、地点)
适合中小型系统 权限变更通常需要重启或刷新缓存

在令牌服务中,RBAC 通常这么落地:

// 令牌解码后,payload 中包含角色信息
{
  "sub": "user_123",
  "roles": ["buyer", "vip"],
  "exp": 1715000000
}

// 网关拦截器判断角色是否在允许列表内
function hasPermission(requiredRole, tokenRoles) {
  return tokenRoles.includes(requiredRole);
}

RBAC 很实用,但它没法回答一个问题——如果你是普通用户,但在下午3点到5点之间可以免审核发布商品,该怎么配置?

嗯,这就引出了另一个模型。

3.2 ABAC——基于属性的权限控制

ABAC 不再依赖“角色”这一个维度。它把权限决策拆成了属性的组合。

属性通常分三类:

  • 主体属性:用户ID、年龄、部门、角色、信用等级
  • 资源属性:资源类型、所有者、分类、地域
  • 环境属性:当前时间、IP地址、设备指纹、网络类型

举个例子:
权限规则:“角色是经理,并且当前时间在工作时间(9:00-18:00),并且资源是‘项目部’的文档,才可以删除。”

你会发现,ABAC 的决策表达式比 RBAC 丰富很多。它不是简单的“角色是否在列表里”,而是一段布尔逻辑。

我的感悟:ABAC 是“条件判断”的一次升级。我刚接触 ABAC 时觉得它无所不能,但很快发现——规则写爽了,性能就崩了。

为什么?因为每次请求进来,都要拉一堆属性,然后逐一评估规则。如果属性来源分散(用户服务、资源服务、环境感知模块),那一次请求可能要等几百毫秒。

我曾经踩过一个坑:在做 SaaS 系统的权限优化时,把所有属性都塞进了 Open Policy Agent(OPA)的输入,结果一次评估要查3个远程接口,QPS 直接腰斩。后来我们做了属性预计算 + 缓存,才算稳住。

优势 劣势
规则灵活,支持多维度判断 属性拉取成本高,容易引发性能瓶颈
不需要大量角色预设 规则编写和维护难,容易产生逻辑漏洞
天然支持数据级权限 调试困难——一条规则正确,多条组合可能出问题

在令牌服务中,ABAC 的常见做法是把属性断言留在令牌的 claims 里,或者把策略引擎外挂到网关层。

// ABAC 策略示例(伪代码)
{
  "effect": "Allow",
  "condition": {
    "subject.role": "manager",
    "resource.owner": "subject.id",
    "environment.time": {"gte": "09:00", "lte": "18:00"}
  }
}

3.3 PBAC——基于策略的权限控制

PBAC 这个名字,你可能听得少一点。其实它和 ABAC 有很多重叠,但侧重点不同。

PBAC 强调策略引擎。策略就是一组声明式的规则,通常由专门的语言(如 XACML、Rego)编写。

它的核心思路是:把权限决策从业务代码中完全抽离出来。

一个典型的 PBAC 流程有4个角色:

  1. 策略决策点(PDP):中心化的评估引擎,接收“主体-资源-动作”三元组,返回 Permit/Deny
  2. 策略执行点(PEP):网关或服务端拦截请求,调用 PDP
  3. 策略信息点(PIP):提供主体、资源、环境等属性数据
  4. 策略管理点(PAP):编写、测试、部署策略的地方

举个例子:

// 使用 Rego(OPA 的策略语言)编写 PBAC 规则
package example.authz

default allow = false

allow {
    input.subject.role == "admin"
}

allow {
    input.subject.role == "user"
    input.resource.type == "article"
    input.action == "read"
}

allow {
    input.subject.role == "vip"
    input.resource.type == "article"
    input.action == "write"
    input.resource.owner == input.subject.id
}

你看,每条规则都是一个独立的 `allow` 子句。只要有一条匹配,就放行。这种“白名单叠加”的写法,比手写 if-else 清爽不止一点点。

我在一个 IoT 平台项目中落地过 PBAC。设备类型 500+、用户角色 20+、还有几十种地理位置规则。如果用 RBAC,角色会多到没人敢改;用 ABAC,每次请求评估要查大量属性。

PBAC 帮我们做了策略缓存 + 批量评估,把平均决策耗时控制在 5ms 以内。

优势 劣势
策略与代码完全解耦,支持热更新 引入独立 PDP,增加架构复杂度
策略可测试、可审计、可版本管理 调试门槛高,需要熟悉策略语言
适合超大规模、跨部门权限场景 简单场景没必要,属于杀鸡用牛刀

一句话总结三者的关系: RBAC 最通用,适合 80% 的系统;ABAC 最灵活,解决 RBAC 够不到的条件判断;PBAC 最强大,把权限决策变成一种可编程的服务。

那么,在令牌服务中我怎么选?

  • 场景简单、用户量不大 → RBAC 就够了。别折腾。
  • 需要根据时间、地点、行为动态控制 → ABAC 是你的菜。
  • 大型企业、多租户、权限规则经常变 → 上 PBAC,把策略交给业务团队自己维护。

下面这张图,直观展示了三种模型的核心区别。

权限模型核心逻辑对比 三种权限模型核心逻辑对比 RBAC 角色-权限映射 ABAC 属性-策略评估 PBAC 策略引擎 主体 → 角色 → 权限 角色 1:N 权限 适合:中小系统 主体 + 资源 + 环境 属性组合评判 适合:动态场景 PDP + PEP + PIP 声明式策略语言 适合:大型/多租户 选择建议 简单场景 → RBAC(省事) 复杂动态条件 → ABAC(灵活) 大规模/策略频繁变更 → PBAC(可编程)

一个实用的建议:我不推荐一上来就上 PBAC。先跑通 RBAC,如果发现角色和条件判断越来越多,再逐步往 ABAC 或 PBAC 迁移。很多大型系统到最后其实都是混合模型——基础权限用 RBAC,特殊场景用 ABAC 或 PBAC 兜底。