FastAPI 权限管理实战:从 ACL 到 RBAC 的那些门道

做后端开发这些年,权限系统大概是最容易被低估、又最容易在生产环境炸锅的模块。刚开始写接口的时候,很多人图省事,直接在函数里写一堆 if user.role == "admin",代码能跑,看着也顺眼。可等到角色一多、资源一复杂,这种写法立马变成一团乱麻,改一个权限规则得满仓库搜索,改漏一处就是安全事故。

FastAPI 官方并没有内置一套完整的权限框架,社区里因此长出了不少解决方案,风格各异。这篇文章挑最有代表性的 fastapi-permissions 库来细聊,顺带把它背后那套源自 Pyramid 框架的权限哲学讲透,再对比一下其他常见做法,帮你判断到底该选哪条路。


fastapi-permissions 是个什么东西

这个库由开发者 holgi 维护,核心卖点是 行级别的权限控制(row level security)。什么意思呢,普通的角色权限只关心用户是谁,而行级权限还要看这个用户操作的是哪一个具体对象。同一个 admin 角色,可能对 A 文章有编辑权,对 B 文章却没有,这种细粒度的判断,光靠角色标签是搞不定的。

作者坦言这套设计几乎是照搬了 Python Web 老牌框架 Pyramid 的安全模型,用他自己的话说算是"一次光明正大的抄袭"。这倒不是贬义,Pyramid 的权限系统在业界口碑一直不错,能把这套成熟思路移植到 FastAPI 生态里,对开发者来说反而是件好事。

项目托管在 GitHub 上,star 数六百多,规模不算庞大但足够稳定,也已经上架 PyPI,一行 pip install fastapi_permissions 就能装上。


四个核心概念,理解了就等于打通任督二脉

这套系统跟 FastAPI 自带的 OAuth2 scopes 是两个思路。scopes 只看用户本身携带了什么权限标签,而 fastapi-permissions 还要结合资源当时的状态一起判断。举个例子,一篇论文在草稿、提交、评审、发表这几个阶段,不同用户能做的事完全不一样,这种场景用 scopes 硬写会很别扭,用这套系统就是天然适配。

整套体系围绕四个概念展开,缺一不可。

graph TD A[Principal 身份标识] -->|拥有| B[Permission 权限动作] B -->|作用于| C[Resource 资源对象] C -->|定义| D[ACL 访问控制列表] D -->|规则由三元组构成| E["《Allow/Deny, Principal, Permission》"] F[configure_permissions 配置函数] -->|注入| G[Permission Depends 依赖] G -->|运行时比对| D

资源(resource) ,指的是任何提供了访问控制列表的对象,可以是一个 Pydantic 模型,也可以就是一个普通的 Python 类。判断依据是它有没有一个叫 __acl__ 的属性或方法。

访问控制列表(ACL) ,说白了就是一份规则清单,每条规则是个三元组,格式是(动作,principal,permission)。动作只有两种,Allow 或者 Deny。系统检查规则时是按顺序从上往下走的,一旦匹配到就立刻生效,不会继续往下看,这个顺序敏感的特性用好了很省事,用不好容易埋坑。

principal ,是身份标识符,可以代表某个具体用户,也可以代表一个角色或群组。习惯上会写成 user:bob 或者 role:admin 这样带前缀的字符串,方便区分类型。系统里还预留了两个特殊值,Everyone 表示所有人无论是否登录都适用,Authenticated 则只对已登录用户生效。

permission ,其实就是一个代表动作的字符串,比如 vieweditdelete ,你可以自己随便定义,系统不做限制。另外还有个通配符 All,代表任意权限都匹配。


手把手怎么用起来

装好包之后,整个使用流程大致分成三步,定义资源、定义用户身份、在路由里挂上权限检查。

第一步,给资源定义 ACL

python 复制代码
from fastapi_permissions import Allow, Deny, Authenticated, Everyone

class Item:
    def __init__(self, name, owner):
        self.name = name
        self.owner = owner

    def __acl__(self):
        return [
            (Allow, Authenticated, "view"),
            (Allow, "role:admin", "edit"),
            (Allow, f"user:{self.owner}", "delete"),
        ]

这段代码翻译成人话就是,任何登录用户都能看这条数据,只有 admin 角色能编辑,只有这条数据的主人才能删除它。注意这里没写任何拒绝规则,是因为系统默认在清单末尾隐含了一条兜底的拒绝,没被明确允许的,一律不通过。

如果 ACL 是固定不变的,还能偷懒直接写成类属性,甚至直接用一个列表当资源用,省得再包一层类。

第二步,定义用户的 principals

系统要求你必须提供一个函数,告诉它当前用户身上挂了哪些身份标签。

python 复制代码
def get_active_principals(user = Depends(get_current_user)):
    if user:
        principals = [Everyone, Authenticated]
        principals.extend(user.principals)  # 比如 ["role:admin", "user:bob"]
    else:
        principals = [Everyone]
    return principals

这一步是整个系统的入口,相当于告诉框架"现在是谁在敲门"。

第三步,配置并在路由里使用

python 复制代码
from fastapi_permissions import configure_permissions

Permission = configure_permissions(get_active_principals)

@app.get("/item/{item_id}")
async def show_item(item: Item = Permission("view", get_item)):
    return {"item": item.name}

configure_permissions 只需要调用一次,拿到的 Permission 已经是包好 Depends() 的产物,直接当依赖注入用就行。路径函数里写 Permission("view", get_item),意思是先用 get_item 这个函数把资源找出来,再检查当前用户对这个资源有没有 view 权限,没有的话框架会自动抛出权限异常,业务代码里完全不用手写判断逻辑。

运行时到底发生了什么

用一张时序图把这个校验过程摊开看,会清楚很多。

sequenceDiagram participant 客户端 participant FastAPI路由 participant Permission依赖 participant get_active_principals participant 资源__acl__ 客户端->>FastAPI路由: 发起请求 /item/1 FastAPI路由->>Permission依赖: 触发权限检查 Permission依赖->>get_active_principals: 获取当前用户principals get_active_principals-->>Permission依赖: 返回《Everyone, Authenticated, user:bob》 Permission依赖->>资源__acl__: 获取资源的ACL规则表 资源__acl__-->>Permission依赖: 返回规则列表 Permission依赖->>Permission依赖: 按顺序逐条匹配principal与permission alt 匹配到Allow规则 Permission依赖-->>FastAPI路由: 放行,返回资源 else 匹配到Deny或无匹配 Permission依赖-->>客户端: 抛出403权限异常 end

这个流程最大的好处是权限规则和业务逻辑彻底分离,改权限只需要改 __acl__ 里的清单,路径函数一行都不用动。


跟其他方案比一比,到底该选谁

社区里其实还有不少同类竞品,各自的适用场景不太一样。有开发者在 FastAPI 官方仓库的一个 issue 里专门讨论过这个问题,提到了 Casbin、OSO、Cerbos 这些第三方策略引擎,也有人干脆自己手写一个 RoleChecker 类当依赖使用,简单粗暴但缺乏细粒度控制。另外还有像 00-Python/FastAPI-Role-and-Permissions 这样的开源脚手架,直接把 JWT 认证、PostgreSQL 数据库、RBAC 表结构打包成一套完整方案,适合想要开箱即用的团队。业界也有专门的教程从零讲解如何在 FastAPI 里搭建完整的 RBAC 体系,覆盖角色管理、权限分配、接口鉴权全流程。

做个简单的横向对比方便你挑选。

方案 核心思路 适用场景 学习成本
FastAPI 原生 scopes 权限绑定在用户身上,不看资源状态 权限规则简单固定的场景 低,官方文档自带
fastapi-permissions 基于 ACL,权限同时看用户和资源状态 需要行级别、按资源状态动态判断的场景 中,需要理解 Pyramid 式概念
自定义 RoleChecker 手写依赖函数直接判断角色 角色种类少,逻辑简单的小项目 低,但扩展性差
00-Python RBAC 脚手架 数据库表驱动的角色权限多对多关系 需要完整用户系统、想快速起步的项目 中,需要吃透数据库表结构
Casbin / OSO / Cerbos 独立策略引擎,语言无关 大型系统、需要跨语言统一策略的场景 较高,引入额外基础设施

如果你的系统权限规则不复杂,用户角色也就那么几种,scopes 完全够用,没必要折腾额外的库。可一旦涉及到"同一个角色对不同数据行有不同权限"这种场景,比如多租户系统、内容审核流程里不同状态对应不同操作权限,fastapi-permissions 这种基于 ACL 的思路就明显更省心,规则集中管理,改动成本低。

要是团队本身就有 Pyramid 开发背景,或者未来考虑用一套策略引擎统一管理多个后端服务的权限,那不妨看看 Casbin 或者 OSO,虽然接入门槛高一点,但长期维护性会更好。


写在最后

权限系统这东西,说难不难,说简单也真不简单。难的地方不在代码怎么写,而在于设计阶段有没有把"谁能对什么东西做什么事"这件事想清楚。fastapi-permissions 的价值就在于逼着你用一种结构化的方式去表达这套规则,资源自己声明访问清单,用户自己声明身份标签,中间由框架去做匹配,这种关注点分离的设计思路,哪怕最后你没用这个库,理解了它也能让你自己写权限系统时少踩很多坑。


参考资料

github.com/holgi/fasta...

github.com/fastapi/fas...

github.com/00-Python/F...

www.permit.io/blog/fastap...

pypi.org/project/fas...

相关推荐
卷无止境1 小时前
软件文档写作中,Agent最常用的十种skill拆解
python·agent·claude
GreenTea9 小时前
深度解读 Anthropic 多智能体报告:更强的模型 ≠ 更好的协调
前端·后端·算法
风流 少年9 小时前
Spring AI 2.0:Memory
java·后端·spring
2603_965148119 小时前
如何解析JSON数据?API返回的商品信息处理教程
开发语言·数据库·python·自动化·json·api
万少10 小时前
给 DeepSeek Harness 装个"应用商店":一条命令,595 个插件随你逛
前端·javascript·后端
云和数据.ChenGuang10 小时前
fastapi的参数剖析
人工智能·深度学习·机器学习·语言模型·状态模式·fastapi
jufeng130711 小时前
【系列:手搓自主 AI Agent:Hermes 架构原理剖析 · 第 8 篇】
python·ai agent·配置系统
circuitsosk11 小时前
跨境电商智能化实战:AI如何赋能客服自动回复、广告智能投放与供应链预测
大数据·人工智能·python·langchain·智能客服
2601_9563198812 小时前
2026年零基础学量化:从看懂示例到写清条件和动作
人工智能·python