FastAPI RBAC 权限管理系统实战

最近学习 FastAPI,我用一个完整的用户权限管理系统把常见知识点串了起来:路由、依赖注入、Pydantic、SQLAlchemy、JWT、前后端联调,以及最重要的 RBAC 权限模型。

项目地址在本机:

1
D:\pycharm_workspace\python_code\FastAPI\fastapi-rbac-admin

这个项目不是只写几个接口做演示,而是做成了一个能跑起来的后台系统:

  • 后端:FastAPI + SQLAlchemy + SQLite + JWT
  • 前端:Vue3 + Vite + Element Plus
  • 权限:用户、角色、权限三层 RBAC
  • 文档:README 和设计文档
  • 数据:内置 admin、editor、viewer 三个演示账号

一、为什么用 RBAC 做 FastAPI 实战

FastAPI 的入门接口很容易写,但真正进入项目实践时,马上会遇到几个问题:

  1. 接口怎么拆分目录?
  2. 数据库模型和接口模型怎么分开?
  3. 登录态怎么维护?
  4. 权限怎么控制到接口级别?
  5. 前端怎么拿到当前用户权限并控制菜单?

RBAC 很适合做练习项目,因为它天然包含多表关系、登录认证、接口鉴权和前后端协作。

RBAC 的核心关系是:

1
用户 User  <->  角色 Role  <->  权限 Permission

用户不直接拥有权限,而是通过角色间接获得权限。比如:

  • admin 拥有全部权限
  • editor 可以查看并维护部分资源
  • viewer 只有只读权限

二、项目结构设计

后端采用分层结构:

1
2
3
4
5
6
7
app/
├── api/ # 路由层,定义接口和权限依赖
├── common/ # 统一响应
├── core/ # 配置、数据库、JWT、密码哈希
├── models/ # SQLAlchemy ORM 模型
├── schemas/ # Pydantic 入参和出参
└── services/ # 业务逻辑

前端采用常见管理后台结构:

1
2
3
4
5
frontend/src/
├── api/ # Axios 请求封装
├── router/ # Vue Router
├── stores/ # Pinia 登录态和权限
└── views/ # 登录页、用户、角色、权限页面

这种拆分的好处是职责清楚:路由只负责 HTTP,Service 处理业务逻辑,Model 映射数据库,Schema 负责数据校验。

三、SQLAlchemy 建模重点

RBAC 有两个典型多对多关系:

  • 用户和角色:user_roles
  • 角色和权限:role_permissions

用户模型中通过 roles 关联角色,角色模型中通过 permissions 关联权限。查询当前用户时,可以一次性加载角色和权限,再汇总出权限标识列表:

1
2
3
def permission_codes(user: User) -> list[str]:
codes = {permission.code for role in user.roles for permission in role.permissions}
return sorted(codes)

这个函数很关键,后端接口鉴权和前端按钮控制都依赖它。

四、JWT 登录流程

登录接口做三件事:

  1. 根据用户名查用户。
  2. 使用 passlib 校验密码哈希。
  3. 生成 JWT 并返回给前端。

前端登录成功后把 token 保存到 localStorage,Axios 请求拦截器自动加上:

1
Authorization: Bearer <token>

后端用 FastAPI 依赖注入解析 token:

1
2
3
def get_current_user(...):
user_id = decode_access_token(credentials.credentials)
return get_user(db, int(user_id))

这一步体现了 FastAPI 依赖注入的优势:接口本身不用重复写解析 token 的代码。

五、接口级权限控制

接口权限通过 require_permission 实现:

1
2
3
4
5
6
@router.get(
"",
dependencies=[Depends(require_permission("user:read"))],
)
def list_users(...):
...

当用户访问接口时,后端会检查当前用户是否拥有 user:read。如果没有,直接返回 403。

这里要注意:前端菜单和按钮隐藏只是体验优化,真正的权限边界一定要放在后端。否则用户可以绕过页面,直接调用接口。

六、前端联调思路

前端用 Pinia 保存当前登录态:

  • token
  • user
  • permissions

菜单和按钮通过 auth.has("user:create") 这类方法判断:

1
2
3
<el-button v-if="auth.has('user:create')" type="primary">
新增用户
</el-button>

这样 viewer 登录时只能看到只读能力,admin 登录时可以看到全部管理按钮。

七、运行方式

后端:

1
2
3
4
5
cd D:\pycharm_workspace\python_code\FastAPI\fastapi-rbac-admin
python -m venv .venv
.\.venv\Scripts\python -m pip install -r requirements.txt
.\.venv\Scripts\python scripts\init_db.py
.\.venv\Scripts\python -m uvicorn app.main:app --reload

前端:

1
2
3
cd D:\pycharm_workspace\python_code\FastAPI\fastapi-rbac-admin\frontend
npm install
npm run dev

演示账号:

用户名 密码 角色
admin admin123 系统管理员
editor editor123 运营编辑
viewer viewer123 只读访客

八、这次实战踩到的点

第一,PowerShell 的执行策略可能会拦截 hexo.ps1。解决方式是使用 npx hexo ...hexo.cmd

第二,前端权限控制不能代替后端权限控制。按钮隐藏只是让页面更清爽,接口仍然必须校验权限。

第三,学习项目使用 SQLite 很合适。它不需要额外安装数据库,能把注意力放在 FastAPI、SQLAlchemy 和 RBAC 模型上。

第四,接口级中文注释很有价值。FastAPI 的 Swagger 会直接展示接口摘要,学习和复盘时非常直观。

九、总结

这个 RBAC 项目把 FastAPI 学习从“写接口”推进到了“做系统”:

  • 用 Pydantic 管参数
  • 用 SQLAlchemy 管数据
  • 用 JWT 管登录态
  • 用依赖注入做鉴权
  • 用 Vue 管理后台完成前后端闭环

下一步可以继续扩展 Alembic 数据库迁移、操作日志、菜单树、刷新令牌和自动化测试,让它更接近真实企业后台。