权限模型全解析(上):别再写 if user.role == "admin" 了,ACL、RBAC四层与ABAC一次讲透
小 A 给后台系统做权限管理,第一版直接用「用户—权限」两张表:用户表里存个 role 字段,代码里到处写 if user.role == "admin"。上线三个月,角色越加越多:运营、客服、财务、外协……每加一个角色,就得改一堆 if 判断。更糟的是「运营组长能看本组数据、运营专员只能看自己数据」这种需求,role 字段根本表达不了。
小 A 这才意识到,权限不是「加个字段」的事,而是一套要专门设计的模型——RBAC、ABAC 这些名词背后,是几十年沉淀下来的权限管理方法论。
标签:RBAC、ABAC、ACL、权限模型、按钮级权限、数据级权限、多租户、PBAC
注:本文为上篇,下篇讲工程落地(五张表、按钮级权限、多租户隔离、PBAC 策略引擎)。全文代码以 FastAPI + SQLAlchemy 为主,关键处附 Django 写法对比。
一、引言:小 A 的三次翻车
第一版:硬编码角色判断
1 | # 第一版:角色写死在代码里 |
问题:角色一多就要改代码,且「运营组长能看本组数据」这种需求根本表达不了。上线三个月,每次加角色都要发版,开发疲于奔命。
第二版:用户—权限点直接关联
1 | # 第二版:每个用户手动挂几十个权限点 |
问题:每个新员工入职,要手动勾选几十个权限点;员工换岗,要逐个调整。维护成本爆炸。有一次管理员给新员工漏勾了关键权限,导致新人三天无法工作——哈哈,就是这么皮。
第三版:引入角色(RBAC)
把权限点打包成角色,用户关联角色而非直接关联权限点。新员工选角色即可,换岗换角色。这是大多数系统的标配。
但很快又遇到新问题:
- 「财务只能看自己部门的财务数据」——权限点
finance:view不够,还要限定「本部门」 - 「外协人员只能在工作时间访问」——还要限定「时间」
- 「敏感操作需要二次审批」——权限不只是「能不能」,还有「在什么条件下能」
- 「财务制单和审核不能是同一人」——要表达角色互斥
这些场景,单纯的角色模型表达不了,需要属性鉴权(ABAC)和角色约束(RBAC2)。
小 A 这才明白:权限不是一步到位的,而是一套从简单到复杂的模型演进。本文就把这套演进讲透——从 ACL 到 RBAC0/1/2/3,再到 ABAC。
二、概念定义:先把话说清楚
2.1 什么是权限模型
定义:权限模型是描述「主体(Subject)对客体(Object)执行动作(Action)」的授权关系的抽象框架,定义了权限如何组织、分配、校验。
通俗讲:权限模型回答的就是一句话——「谁能对什么东西做什么」。它规定了「谁」(用户/角色)、「什么东西」(资源)、「做什么」(读/写/删)这三者怎么关联、怎么判断。
你可以把权限模型想象成餐厅的点菜规则:谁(顾客)可以点(动作)什么菜(资源),由菜单和套餐结构(权限模型)决定,而不是每次点菜都把厨师叫出来问一遍。
2.2 三个核心要素
| 要素 | 说明 | 例子 |
|---|---|---|
| 主体(Subject) | 发起操作的人/服务 | 用户、服务账号 |
| 客体/资源(Object/Resource) | 被操作的对象 | 订单、用户、菜单、按钮 |
| 动作(Action) | 对资源的操作 | 查看、新增、修改、删除 |
权限的本质是一句三元组:主体 是否能 对客体 执行 动作。
在 Python 代码里,这个三元组最直白的表达就是一个函数:
1 | def can(user: User, action: str, resource: str) -> bool: |
后面所有模型,本质上都是在回答「这个函数怎么实现才优雅、可维护」。
2.3 主流权限模型一览
| 模型 | 全称 | 核心思想 | 适用 |
|---|---|---|---|
| ACL | Access Control List | 直接给资源列「谁能访问」清单 | 文件系统、简单资源 |
| RBAC | Role-Based Access Control | 用户关联角色,角色关联权限 | 绝大多数业务系统 |
| ABAC | Attribute-Based Access Control | 基于主体/客体/环境属性动态判断 | 复杂条件、数据级权限 |
| PBAC | Policy-Based Access Control | 基于策略引擎统一决策 | 复杂企业、合规场景 |
2.4 认证 vs 授权
权限模型管的是「授权」(AuthZ),与「认证」(AuthN)不同:
| 维度 | 认证(AuthN) | 授权(AuthZ) |
|---|---|---|
| 解决问题 | 你是谁 | 你能做什么 |
| 典型问题 | 登录、身份验证 | 权限校验、资源访问控制 |
| 常见手段 | 账号密码、SSO、OIDC、JWT | RBAC、ABAC、ACL |
关系:认证是授权的前提——先知道「你是谁」,才能判断「你能做什么」。
用 FastAPI 的话说,认证是「拿到 token 解析出 user」,授权是「拿着 user 去查他能不能干这件事」:
1 | # 认证:你是谁?—— 从 token 解出当前用户 |
本文聚焦授权(权限模型),认证部分可参考本博客的 JWT / OAuth2 相关文章。
三、阐述背景:权限模型是怎么一步步演进来的
每一种新模型的诞生,都是前一种模型遇到了瓶颈。
3.1 演进时间线
graph LR
A["ACL<br/>访问控制列表"] --> B["RBAC0<br/>基础三层"]
B --> C["RBAC1<br/>角色继承"]
C --> D["RBAC2<br/>角色约束"]
D --> E["RBAC3<br/>继承+约束"]
E --> F["ABAC<br/>属性鉴权"]
F --> G["PBAC<br/>策略引擎"]
style A fill:#ffcdd2,stroke:#c62828
style B fill:#c8e6c9,stroke:#2e7d32
style C fill:#c8e6c9,stroke:#2e7d32
style D fill:#c8e6c9,stroke:#2e7d32
style E fill:#c8e6c9,stroke:#2e7d32
style F fill:#bbdefb,stroke:#1565c0
style G fill:#ffe0b2,stroke:#ef6c00
3.2 每一代解决的痛点
| 演进 | 解决的痛点 | 例子 |
|---|---|---|
| ACL → RBAC | ACL 用户直接关联资源,人员一多就难维护 | 文件系统给每个用户设权限 vs 用角色批量授权 |
| RBAC0 → RBAC1 | 角色不能继承,重复定义 | 「运营」和「运营组长」权限大量重叠,需继承 |
| RBAC1 → RBAC2 | 无法表达职责分离 | 财务「制单」和「审核」不能是同一人 |
| RBAC → ABAC | 无法表达条件性权限 | 「只能看本部门数据」、「只能工作时间访问」 |
| ABAC → PBAC | 规则散落各处难维护 | 规则集中到策略引擎,统一管理审计 |
3.3 为什么 RBAC 是主流
绝大多数业务系统选 RBAC,因为:
- 契合组织结构:企业本身就是按角色(岗位)划分职责的,RBAC 映射自然
- 批量管理高效:角色对应岗位,人员变动只换角色,权限自动调整
- 简单够用:80% 的权限需求用「有没有某权限点」就能表达
- 成熟生态:Python 这边有
django-guardian、Casbin(pycasbin)、FastAPI 生态的各类require依赖,都内置 RBAC 支持
而 ABAC、PBAC 是在 RBAC 基础上的补充——RBAC 管「能不能调这个接口」,ABAC 管「能看哪些数据」,PBAC 管「复杂条件下统一决策」。
选型口诀:先 RBAC,数据范围不够上 ABAC,规则多到管不住上 PBAC。别一上来就搞策略引擎,那是杀鸡用牛刀。
四、类比:写字楼的门禁体系
把权限模型类比成写字楼的门禁管理:
| 现实世界 | 权限模型对应 |
|---|---|
| 给每个人单独配每扇门的钥匙 | ACL(每人直接关联每扇门) |
| 给员工发「部门工牌」,凭工牌进相应楼层 | RBAC(角色 = 工牌) |
| 经理工牌自动包含员工工牌的权限 | RBAC1(角色继承) |
| 财务室的「制单」卡和「审核」卡不能同人持有 | RBAC2(职责分离约束) |
| 进机房还要刷指纹 + 只能工作日进 | ABAC(多重属性条件) |
| 整套门禁规则在中央系统统一配置 | PBAC(策略引擎) |
几个关键点:
- ACL 是「门找钥匙」:每扇门维护一张「谁能进」的清单,人一多清单爆炸
- RBAC 是「人找工牌」:人持工牌,工牌对应能进的门,批量管理
- ABAC 是「现场判断」:不只是看工牌,还看时间、地点、指纹等属性,动态决策
- PBAC 是「中央调度」:所有门禁规则在中央系统配置,各门只管「问中央」
记不住的时候就想门禁:钥匙(ACL)→ 工牌(RBAC)→ 工牌+指纹+时间(ABAC)→ 中央大脑(PBAC)。
五、正反例子:眼见为实
5.1 反例:硬编码角色判断
1 | # ❌ 反例:角色与代码耦合 |
问题:
- 角色与代码耦合,新增角色要改代码、重新发版
- 一个用户只能一个角色(
role字段单值),无法支持「既是运营又是客服」 - 「运营组长能看本组数据」表达不了
5.2 反例:用户直接关联权限点
1 | # ❌ 反例:每个用户手动勾选几十个权限点 |
问题:新员工入职要手动勾几十个权限点,换岗要逐个调整,维护成本爆炸。且无法批量管理——所有运营人员都要加 order:view 时,要逐个加。
5.3 正例:RBAC 标准三层
graph LR
U1["用户:小王"] --> R1["角色:运营专员"]
U2["用户:小李"] --> R1
U2 --> R2["角色:客服"]
R1 --> P1["权限:order:view"]
R1 --> P2["权限:order:edit"]
R2 --> P3["权限:ticket:reply"]
style U1 fill:#e1f5fe
style U2 fill:#e1f5fe
style R1 fill:#fff9c4
style R2 fill:#fff9c4
style P1 fill:#f3e5f5
style P2 fill:#f3e5f5
style P3 fill:#f3e5f5
用户关联角色,角色关联权限点。新员工选「运营角色」即获全部运营权限;所有运营要加新权限点,改角色即可,自动对所有运营生效。
用 Python 表达,就是三层多对多关系:
1 | # 正例:用户 → 角色 → 权限,三层解耦 |
5.4 正例:ABAC 处理条件权限
1 | # 「只能看本部门订单」——RBAC 表达不了,用 ABAC |
权限不只是「能不能 order:view」,还要「只能看本部门的」——这要靠**属性(部门)**动态判断,是 ABAC 的范畴。
Django 对比:Django 自带的
Permission只到「模型+动作」粒度,数据范围(本部门/本人)同样要自己扩展——通常在QuerySet.for_user()里做,思路完全一致。
六、核心一:ACL 详解
6.1 ACL 的原理
ACL(Access Control List,访问控制列表)是最朴素的权限模型:每个资源维护一张「谁能对它做什么」的清单。
1 | 资源:/file/报告.docx |
翻译成人话:这份报告,1 号用户能看能改,2 号用户只能看,财务组全员能看能改。
6.2 ACL 的适用与局限
| 优点 | 缺点 |
|---|---|
| 直观,易于理解 | 资源多时,每资源维护清单爆炸 |
| 细粒度,可精确到单个资源 | 人员变动要逐资源调整 |
| 适合资源少、相对固定 | 无法批量授权 |
适用场景:
- 文件系统(Linux 文件 owner/group/other 权限)
- 云资源 IAM(每个资源单独配策略)
- 对象存储(OSS Bucket Policy)
这些「资源少、需精细到单个资源」的场景 ACL 更合适。
不适用:企业后台系统(用户多、资源多、人员常变动),ACL 维护成本爆炸,需用 RBAC。
一个直观的心算:公司有 500 个员工、2000 张报表。用 ACL,最多 100 万条记录,每天有人入职离职都要动一批;用 RBAC,500 个员工 × 平均 2 个角色 = 1000 条关联,报表只挂给角色,改一次全生效。这就是差距。
6.3 ACL 与 RBAC 的本质区别
graph TB
subgraph ACL["ACL:资源找用户(resource → user)"]
direction LR
RA["报表A"] --> U1["小王"]
RA --> U2["小李"]
RB["报表B"] --> U2
RC["报表C"] --> U3["小张"]
end
subgraph RBAC["RBAC:用户找资源(user → role → resource)"]
direction LR
U4["小王"] --> R1["运营角色"] --> RD["运营可见的报表"]
U5["小李"] --> R1
U5 --> R2["客服角色"] --> RE["客服可见的报表"]
end
style RA fill:#ffcdd2
style RB fill:#ffcdd2
style RC fill:#ffcdd2
style R1 fill:#c8e6c9
style R2 fill:#c8e6c9
style RD fill:#bbdefb
style RE fill:#bbdefb
- ACL 是「资源找用户」——每个资源列能访问的用户
- RBAC 是「用户找资源」——用户持角色,角色对应资源权限
视角转换的意义:企业人员变动频繁、角色相对固定,「用户找资源」更符合管理习惯。人走了换角色,资源侧一行不用改。
七、核心二:RBAC 四层模型详解
RBAC 标准分四层(NIST RBAC,INCITS 359-2004),逐层增强。
7.1 RBAC0:基础三层
用户—角色—权限三层多对多。这是最常用的形态,80% 的系统到这层就够。
核心数据结构:
- 用户表(
sys_user) - 角色表(
sys_role) - 权限表(
sys_permission) - 用户-角色关联表(
sys_user_role) - 角色-权限关联表(
sys_role_permission)
1 | # RBAC0 的本质:两个多对多映射 |
下篇 9.1 节会给出完整的五张表 SQL,这里先建立直觉。
7.2 RBAC1:角色继承
子角色继承父角色的所有权限,可额外加权限。
价值:避免重复定义——「组长」自动包含「专员」权限,再加管理权限,不用把专员的权限点再抄一遍。
实现:角色表加 parent_id,加载权限时递归把父角色权限合并。
1 | def expand_with_parents(role_ids: list[int], find_parent) -> set[int]: |
注意事项:
- 继承要避免环(A 继承 B,B 又继承 A)——上面代码用
set.add的返回值天然挡住已存在节点,环不会死循环 - 多继承会复杂化,多数系统只支持单继承(一个角色一个父角色)
- 继承链不宜过深,否则权限溯源困难(「小王为什么能删订单?」要往上翻五层)
7.3 RBAC2:角色约束
最常见的是职责分离(SoD, Separation of Duties):
| 约束 | 例子 |
|---|---|
| 静态职责分离 | 「制单」和「审核」角色不能同一人持有 |
| 动态职责分离 | 同一人可持有,但同一业务流程中不能同时扮演两个角色 |
| 基数约束 | 一个用户最多 3 个角色 |
| 先决条件 | 必须先有「运营专员」才能授「运营组长」 |
静态职责分离(分配角色时拦截):
1 | class RoleConflictError(BusinessError): |
动态职责分离(业务执行时拦截):
1 | def approve_order(order_id: int, user_id: int) -> None: |
静态 vs 动态的区别:
| 类型 | 拦截时机 | 灵活性 | 实现复杂度 |
|---|---|---|---|
| 静态 SoD | 角色分配时 | 低(同人不能持有互斥角色) | 简单 |
| 动态 SoD | 业务执行时 | 高(可持有,但流程中不能同人) | 复杂 |
通俗理解:静态 SoD 是「两张卡不能办在同一人名下」;动态 SoD 是「卡都能办,但今天刷卡进财务室时,就不能再刷卡进审核室」。
7.4 RBAC3:RBAC1 + RBAC2
同时支持继承和约束。实际企业级 RBAC 都是 RBAC3。
graph TB
RBAC3["RBAC3:企业级标配<br/>继承 + 约束"]
RBAC3 --> RBAC1["RBAC1:角色继承<br/>parent_id 递归合并"]
RBAC3 --> RBAC2["RBAC2:角色约束<br/>静态/动态职责分离"]
RBAC1 --> RBAC0["RBAC0:基础三层<br/>用户-角色-权限多对多"]
RBAC2 --> RBAC0
style RBAC3 fill:#c8e6c9,stroke:#2e7d32
style RBAC1 fill:#e8f5e9
style RBAC2 fill:#e8f5e9
style RBAC0 fill:#f1f8e9
7.5 RBAC 的局限性
RBAC 表达「能不能」很自然,但以下场景力不从心:
| 场景 | RBAC 表达 | 问题 |
|---|---|---|
| 只看本部门数据 | ❌ | 权限点不包含「部门」维度 |
| 只看自己创建的 | ❌ | 权限点不包含「所有者」维度 |
| 工作时间才能访问 | ❌ | 权限点不包含「时间」维度 |
| 密级以上才看 | ❌ | 权限点不包含「密级」维度 |
这些「条件性权限」需要 ABAC。
换个说法:RBAC 是开关(开/关二选一),ABAC 是旋钮(按条件连续调节)。要「本部门的」「工作时间的」「密级低于我的」,开关不够用。
八、核心三:ABAC 属性鉴权详解
8.1 ABAC 的四类属性
| 属性类型 | 例子 |
|---|---|
| 主体属性 | 部门、职级、用户组、Clearance 级别 |
| 客体属性 | 资源归属部门、密级、所有者、创建时间 |
| 操作属性 | 读/写/删 |
| 环境属性 | 当前时间、IP、设备、地理位置 |
判断逻辑:主体属性 × 客体属性 × 环境属性 → 是否允许。
1 | def allow(subject: Subject, obj: Resource, env: Environment) -> bool: |
8.2 典型 ABAC 场景
| 场景 | 规则 |
|---|---|
| 只看本部门数据 | 主体.dept == 客体.dept |
| 只看自己创建的 | 主体.uid == 客体.creator |
| 工作时间才能访问 | 环境.time ∈ 9:00-18:00 |
| 密级以上才看 | 主体.clearance >= 客体.level |
| 特定 IP 网段 | 环境.ip ∈ 内网段 |
| 非工作时间需 MFA | 环境.time ∈ 非工作时间 AND 主体.MFA 通过 |
8.3 ABAC 的实现方式
ABAC 在工程上有三种落地方式:
| 方式 | 实现 | 优缺点 |
|---|---|---|
| 装饰器 + 依赖注入 | @data_scope 装饰器 + 中间件注入条件 |
简单,适合数据级权限 |
| 规则引擎 | simpleeval / Lark 表达式求值 |
灵活,规则可配置 |
| 策略引擎 | OPA / Cedar 统一决策 | 集中管理,适合复杂合规 |
绝大多数系统用第一种(装饰器 + 依赖注入)就够了,复杂场景才上规则/策略引擎。
FastAPI 天生适合第一种:
Depends就是现成的「切面」。Django 对应的位置是中间件(middleware)或自定义QuerySet方法。
8.4 数据级权限实现
ABAC 最常见的落地是数据级权限——同一个 order:view 权限,不同人看到的数据范围不同。
数据范围定义:
1 | from enum import Enum |
装饰器定义(对应 Java 的注解):
1 | from contextvars import ContextVar |
SQLAlchemy 事件自动注入(对应 Java 的 MyBatis 拦截器):
1 | from sqlalchemy import event |
业务接口只需装饰器:
1 |
|
这就是「同一接口、不同人不同数据范围」的实现——RBAC 管「能不能调这个接口」,ABAC 管「能看哪些数据」。
注意:上面的字符串拼接只是演示原理,生产环境有 SQL 注入风险。正确做法是用 SQLAlchemy 的
with_loader_criteria在 ORM 层注入参数化条件,而不是改写 SQL 文本:
1
2
3 execute_state.statement = execute_state.statement.options(
with_loader_criteria(Order, lambda cls: cls.dept_id == user.dept_id)
)下篇 11.2 节讲多租户时会给出更完整的写法。
Django 对比:等价写法是在自定义 Manager/QuerySet 上提供
for_user(user)方法,或用django-guardian的对象级权限;没有 AOP,但「装饰器/中间件 + 统一注入」的思路一致。
8.5 行级 + 列级:不只限定「看哪些行」,还要限定「看得见哪些列」
8.4 解决的是行级权限(row-level):同样一个 /order/list,财务只返回本部门的行、运营只返回自己创建的行。但真实需求往往再进一层——
小 A 的新需求:财务专员能看到本部门订单的单号、金额、状态,但银行卡号、成本价这两列只有财务主管可见;审计人员能看全公司所有订单,但只能看单号和状态两列,金额一律不给看。
这就是列级权限(column-level):行你有资格看,但每行里只有部分字段有资格看。它和行级是两个正交的维度,必须同时生效:
| 维度 | 控制什么 | 实现位置 | 本文章节 |
|---|---|---|---|
| 行级(row) | 返回哪些记录 | SQL WHERE(@data_scope) |
8.4 |
| 列级(column) | 每条记录里露出哪些字段 | SELECT 投影 / 响应序列化 |
8.5 |
两个维度组合出的典型矩阵(注意行范围和可见列不是一回事——审计行范围最广,列却最少):
| 角色 | 行范围 | 可见列 | 敏感值处理 |
|---|---|---|---|
| 财务主管 | 本部门及子部门 | 全部列 | 银行卡明文 |
| 财务专员 | 本部门 | 除 bank_card、cost 外 |
银行卡不返回 |
| 运营 | 本部门 | order_no、status、amount |
— |
| 审计 | 全部(ALL) |
仅 order_no、status |
无金额列 |
列级权限的三个实现层次:
| 层次 | 做法 | 适用 |
|---|---|---|
| 数据库层 | 视图(VIEW)只暴露安全列 / PostgreSQL 列级 GRANT |
列组合少且固定 |
| ORM 层 | 查询时只 select() 允许的列 |
单表查询,性能最优 |
| 序列化层(推荐) | 返回前按角色裁剪字段 | 跨表 join、组合多,最灵活 |
实现(序列化层投影 + 敏感值脱敏):
1 | # 列级权限:角色 → 可见列;None 表示不限制(全部列) |
行级 + 列级组合的完整接口:
1 |
|
请求进来到出去,数据被两道闸依次过滤:
graph LR
A["请求<br/>GET /order/list"] --> B["① 行级过滤<br/>WHERE dept_id = 我的部门"]
B --> C["② 列级投影<br/>按角色裁剪字段 + 脱敏"]
C --> D["响应<br/>只剩有资格看的数据"]
style B fill:#c8e6c9,stroke:#2e7d32
style C fill:#bbdefb,stroke:#1565c0
Django / DRF 对比:等价做法是在 Serializer 里按角色动态删字段——
1 | class OrderSerializer(serializers.ModelSerializer): |
安全提醒(和下篇 16.1 呼应):列级权限必须在后端强制。前端把银行卡列
v-if="hasPerm"隐藏只是视觉效果——攻击者打开浏览器开发者工具看接口原始响应,字段一个不少。前端管显示,后端管安全,列级和接口级是同一条铁律。
对比专栏:Casbin 如何用一个模型文件表达 ACL / RBAC / ABAC
前面六、七、八三章把 ACL、RBAC、ABAC 分开讲,仿佛是三套互不相干的系统。开源策略库 Casbin(Python 绑定为 pycasbin)用事实证明:它们可以是同一套引擎的不同配置。
Casbin 把权限判断拆成两半:
- 模型(
model.conf):定义「怎么判」——请求长什么样、策略长什么样、有没有角色关系、匹配公式是什么 - 策略(
policy.csv或数据库表):定义「判什么」——具体的授权数据
判断永远只有一句话:结果 = matchers(请求, 策略)。换权限模型 = 改 matchers 公式,业务代码一行不动。
从 ACL 到 RBAC,只差一个 g()
ACL(对应第六章)——策略行直接写用户,matcher 全等比较:
1 | # acl_model.conf |
1 | # acl_policy.csv —— 每条策略就是 ACL 清单里的一行 |
RBAC(对应第七章)——追加 [role_definition] 一段,matcher 里把「用户全等」换成「角色函数」:
1 | # rbac_model.conf:相对 acl 只多了 role_definition 和 g() |
1 | # rbac_policy.csv —— 策略写角色,g() 负责「谁是什么角色」 |
g(r.sub, p.sub)就是「用户 → 角色」的归属判断,p.sub存的不再是用户而是角色- RBAC1 的角色继承也只需多几行
g:g, alice, group_leader+g, group_leader, operator,g()自动求传递闭包 keyMatch2让/order/*通配一组接口(接口级权限,对应下篇第十章)
ABAC:matcher 里直接写属性表达式
属性鉴权(对应 8.1~8.5)在 Casbin 里不是新机制,只是 matcher 换成了属性比较——请求对象携带属性,公式现场计算:
1 | # abac_model.conf:角色归属 + 部门属性,两个条件同时成立才放行 |
1 | p, operator, order:view, GET |
1 | import casbin |
三个结果全部符合预期:有角色 AND 同部门 才能看——这正是上篇 8.1 「主体属性 × 客体属性 → 决策」的公式化表达。
实现细节(踩坑记录):pycasbin 的
g()角色函数按原始值比较,sub 传自定义对象时角色匹配会失效。所以混合模型里角色用r.sub.name(取到字符串)、属性用r.sub.dept(取到属性值),各取所长——上面的 matcher 就是验证过的可用写法。
本文模型 ↔ Casbin 表达 对照表
| 本文模型 | Casbin 的表达 | 与上一模型的差异 |
|---|---|---|
| ACL(六章) | r.sub == p.sub && ... 全等比较 |
基准 |
| RBAC0(7.1) | g = _, _ + g(r.sub, p.sub) |
策略改写角色,g() 关联用户 |
| RBAC1(7.2) | 多行 g 形成角色链 |
继承 = g() 的传递闭包 |
| RBAC2(7.3) | ⚠️ 非原生,职责分离需业务层把关 | Casbin 只管「能不能」,不管「能不能同时」 |
| ABAC(八章) | matcher 写属性表达式,sub/obj 携带属性 | 判断从「查表」变「算式」 |
| 接口通配(下篇十章) | keyMatch2(r.obj, p.obj) |
路径模式匹配 |
| 多租户(下篇十一章) | 策略加域列 / g = _, _, _ 域模型 |
见下篇 11 章 |
这张表就是 Casbin 视角下的「权限模型全家福」:模型文件的自由度 = 权限模型的表达能力,而 RBAC2 这类「组合约束」依然要回到业务代码(上篇 7.3 的 assign_role / approve_order)——引擎负责判断,约束负责时机,各司其职。
三分钟上手 pycasbin
1 | pip install casbin |
1 | import casbin |
Casbin 是进程内的嵌入式引擎(函数调用级延迟),适合中小系统直接用;当下篇十二章的规则多到需要独立服务、跨系统统一决策时,再升级到 OPA 这类外置策略引擎。两者的取舍在下篇展开。
未完待续……
下篇(权限系统落地实战)将讲解:RBAC 五张表结构、权限编码规范、权限加载与缓存、按钮级/菜单级权限、多租户隔离、PBAC 策略引擎、分场景选型、pytest 单元测试与常见误区。