Skip to content

第 12 章 认证与权限(JWT + RBAC) ​

学习目标 ​

学完本章,你应该能:

  1. 理解 JWT 认证的原理和 fba 中的用法
  2. 理解 RBAC 权限控制的原理
  3. 掌握如何在接口上添加认证和权限校验

一、认证 vs 授权 ​

先分清两个概念:

  • 认证(Authentication):确认"你是谁"。登录时验证用户名密码,发个 token 证明身份。
  • 授权(Authorization):确认"你能干什么"。验证通过后,再判断你有没有权限做某件事。

fba 用 JWT 做认证,用 RBAC 做授权。

认证与授权的关系 ​

二、JWT 认证 ​

什么是 JWT ​

JWT(JSON Web Token) 是一种身份令牌。登录成功后,服务器发给你一个加密的字符串(token),之后每次请求带上它,服务器就能识别你的身份。

JWT 认证时序图 ​

fba 自研了高性能的 JWT 中间件,并用 Redis + Rust 库缓存和解析用户信息,把性能影响降到最低。

fba 中的用法 ​

fba 提供了 DependsJwtAuth 依赖,用于接口鉴权:

python
from backend.common.security.jwt import DependsJwtAuth

@router.get('/hello', summary='你好', dependencies=[DependsJwtAuth])
async def hello():
    ...

加了 dependencies=[DependsJwtAuth] 后,这个接口就要求请求头携带有效的 Bearer Token,否则拒绝访问。

获取 token 的方式 ​

  1. Swagger 登录:启动后进入 Swagger 文档,可通过调试接口快速获取 token(无需验证码)
  2. 验证码登录:配合前端实现,用 fast_captcha 生成验证码后登录

三、RBAC 权限控制 ​

什么是 RBAC ​

RBAC(Role-Based Access Control,基于角色的权限控制) 的核心思想:

用户 → 角色 → 权限
  • 先创建角色(如"管理员"、"编辑"、"访客")
  • 给角色分配权限
  • 给用户分配角色

这样不用逐个用户配置权限,管理角色即可。

RBAC 模型 ​

fba 的角色菜单模式 ​

fba 默认使用角色菜单实现 RBAC。核心是给接口加一个"权限标识":

python
@router.post(
    '',
    summary='xxx',
    dependencies=[
        Depends(RequestPermission('sys:api:add')),  # 权限标识
        DependsRBAC,
    ],
)

原理:

  • sys:api:add 是权限标识,对应菜单里的"权限标识"字段
  • 只有当用户拥有对应菜单(权限)时,才能访问这个接口

RBAC 鉴权流程 ​

配置步骤 ​

  1. 接口加依赖:在接口上添加 RequestPermission('xxx:xxx:xxx') 和 DependsRBAC
  2. 菜单配置权限标识:在系统菜单里设置权限标识,与接口中的保持一致

Casbin 方案(进阶) ​

fba 还支持用 Casbin 做更灵活的权限控制(作为外置插件)。Casbin 是 Go 生态流行的权限方案,可通过模型定义多种控制规则。需要时从插件市场获取并启用。

默认用角色菜单即可满足大多数需求,Casbin 适合更复杂的权限模型。

四、数据权限(进阶) ​

除了接口级权限,fba 还支持数据权限——控制用户能看到哪些数据(比如部门经理只能看本部门的数据)。

什么是数据权限 ​

前面讲的 RBAC 回答的是"你能调哪个接口",而数据权限回答的是"你能看哪些数据"。这是更细粒度的权限控制。

fba 的数据权限方案 ​

fba 内置了 数据范围(DataScope)+ 数据规则(DataRule) 的方案,实现动态、可配置的数据过滤。核心关系链:

用户 → 角色 → 数据范围 → 数据规则
  • DataScope(数据范围):数据规则的逻辑分组,一个范围可包含多条规则
  • DataRule(数据规则):定义具体的过滤条件,由「模型、字段、运算符、表达式、值」组成

数据权限过滤流程 ​

使用方式 ​

数据权限的使用很简单,就是"接口层注入 → CRUD 层透传":

python
# 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)。

三个层次的权限(回顾) ​

五、白名单配置 ​

有些接口不需要认证(如登录接口、健康检查),可通过配置白名单:

python
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)