第 12 章 认证与权限(JWT + RBAC)
学习目标
学完本章,你应该能:
- 理解 JWT 认证的原理和 fba 中的用法
- 理解 RBAC 权限控制的原理
- 掌握如何在接口上添加认证和权限校验
一、认证 vs 授权
先分清两个概念:
- 认证(Authentication):确认"你是谁"。登录时验证用户名密码,发个 token 证明身份。
- 授权(Authorization):确认"你能干什么"。验证通过后,再判断你有没有权限做某件事。
fba 用 JWT 做认证,用 RBAC 做授权。
认证与授权的关系
二、JWT 认证
什么是 JWT
JWT(JSON Web Token) 是一种身份令牌。登录成功后,服务器发给你一个加密的字符串(token),之后每次请求带上它,服务器就能识别你的身份。
JWT 认证时序图
fba 自研了高性能的 JWT 中间件,并用 Redis + Rust 库缓存和解析用户信息,把性能影响降到最低。
fba 中的用法
fba 提供了 DependsJwtAuth 依赖,用于接口鉴权:
from backend.common.security.jwt import DependsJwtAuth
@router.get('/hello', summary='你好', dependencies=[DependsJwtAuth])
async def hello():
...加了
dependencies=[DependsJwtAuth]后,这个接口就要求请求头携带有效的 Bearer Token,否则拒绝访问。
获取 token 的方式
- Swagger 登录:启动后进入 Swagger 文档,可通过调试接口快速获取 token(无需验证码)
- 验证码登录:配合前端实现,用
fast_captcha生成验证码后登录
三、RBAC 权限控制
什么是 RBAC
RBAC(Role-Based Access Control,基于角色的权限控制) 的核心思想:
用户 → 角色 → 权限- 先创建角色(如"管理员"、"编辑"、"访客")
- 给角色分配权限
- 给用户分配角色
这样不用逐个用户配置权限,管理角色即可。
RBAC 模型
fba 的角色菜单模式
fba 默认使用角色菜单实现 RBAC。核心是给接口加一个"权限标识":
@router.post(
'',
summary='xxx',
dependencies=[
Depends(RequestPermission('sys:api:add')), # 权限标识
DependsRBAC,
],
)原理:
sys:api:add是权限标识,对应菜单里的"权限标识"字段- 只有当用户拥有对应菜单(权限)时,才能访问这个接口
RBAC 鉴权流程
配置步骤
- 接口加依赖:在接口上添加
RequestPermission('xxx:xxx:xxx')和DependsRBAC - 菜单配置权限标识:在系统菜单里设置权限标识,与接口中的保持一致
Casbin 方案(进阶)
fba 还支持用 Casbin 做更灵活的权限控制(作为外置插件)。Casbin 是 Go 生态流行的权限方案,可通过模型定义多种控制规则。需要时从插件市场获取并启用。
默认用角色菜单即可满足大多数需求,Casbin 适合更复杂的权限模型。
四、数据权限(进阶)
除了接口级权限,fba 还支持数据权限——控制用户能看到哪些数据(比如部门经理只能看本部门的数据)。
什么是数据权限
前面讲的 RBAC 回答的是"你能调哪个接口",而数据权限回答的是"你能看哪些数据"。这是更细粒度的权限控制。
fba 的数据权限方案
fba 内置了 数据范围(DataScope)+ 数据规则(DataRule) 的方案,实现动态、可配置的数据过滤。核心关系链:
用户 → 角色 → 数据范围 → 数据规则- DataScope(数据范围):数据规则的逻辑分组,一个范围可包含多条规则
- DataRule(数据规则):定义具体的过滤条件,由「模型、字段、运算符、表达式、值」组成
数据权限过滤流程
使用方式
数据权限的使用很简单,就是"接口层注入 → CRUD 层透传":
# 1. 接口层:通过 DataPermissionFilter 注入
@router.get('')
async def get_dept_tree(
db: CurrentSession,
data_filter: Annotated[ColumnElement[bool], Depends(DataPermissionFilter(Dept))],
):
return await dept_service.get_tree(db, data_filter=data_filter)
# 2. CRUD 层:把 data_filter 作为查询条件透传
async def get_all(self, db, data_filter, ...):
return await self.select_models_order(db, 'sort', 'asc', data_filter, **filters)核心优势:业务表无需强制包含特定字段(如 dept_id、created_by),过滤条件完全可配置,还支持动态模板变量(如
${user_id}当前用户 ID)。
三个层次的权限(回顾)
五、白名单配置
有些接口不需要认证(如登录接口、健康检查),可通过配置白名单:
TOKEN_REQUEST_PATH_EXCLUDE = [...] # 这些路径不校验 token注意:白名单内的路由,
request.user将不可用(因为没有解析 token)。
六、常见疑问
Q1:认证和授权为什么分开? 因为它们是两回事。一个人"登录了"(认证通过)不代表他"有权限做某事"(授权通过)。分开设计更灵活。
Q2:权限标识 sys:api:add 的格式有什么含义? 通常是 模块:资源:操作 的三段式,如 sys:api:add = 系统模块:接口资源:新增操作。具体按团队约定。
本章小结
关键要点回顾:
- 认证确认"你是谁"(JWT),授权确认"你能干什么"(RBAC)
- JWT:登录拿 token,后续请求带 token,用
DependsJwtAuth校验 - RBAC:用户 → 角色 → 权限,接口加
RequestPermission('xxx:xxx:xxx')权限标识 - 白名单路径不校验 token
- 数据权限:DataScope + DataRule 控制"你能看哪些数据",接口层注入 → CRUD 层透传
下一步: 第 13 章讲缓存系统,解决接口性能问题。
对应官方文档:
JWT(backend/reference/jwt.html)、RBAC(backend/reference/RBAC.html)、数据权限(backend/reference/data-permission.html)