RBAC需建模用户、角色、权限及两个关联表四张表;请求前用before_request动态加载权限至g.permissions并缓存;装饰器仅校验权限字符串是否存在,不处理业务逻辑或未登录跳转。

Flask 中如何用 SQLAlchemy 建模 RBAC 的核心四张表
RBAC 不是靠装饰器硬编码出来的,得先有用户、角色、权限、关联关系这四层数据结构。缺一不可,否则后续所有逻辑都会绕弯或漏权限。
-
User表存账号信息(id,username,password_hash),不直接存角色,只通过中间表关联 -
Role表只存角色名(如"admin","editor"),不存具体权限,避免耦合 -
Permission表存原子级操作标识(如"user:read","post:delete"),字符串格式便于校验和扩展 - 两个关联表:
user_roles(user_id→role_id)和role_permissions(role_id→permission_name),用db.Table定义,别写成模型类
注意:不要把权限塞进角色字段里(比如 JSON 字段存列表),那样查权限要全表扫描,也难做细粒度校验。
如何在请求前动态加载并缓存用户权限
每次请求都查数据库拿权限?太慢。但全放 session 里又难同步更新。折中方案是:登录后查一次,存在 g.permissions,并设 TTL 短期缓存(比如 5 分钟)。
- 用
@app.before_request钩子,先检查current_user.is_authenticated,再查user.roles→role.permissions拼出 flat 列表 - 权限列表建议存为
set而非list,后续用"post:write" in g.permissions查更快 - 别用 Redis 全局缓存用户权限——除非你有主动失效机制;否则用户改了角色,旧权限还在缓存里,风险很大
- 示例片段:
g.permissions = set(p.name for r in current_user.roles for p in r.permissions)
装饰器 @require_permission 怎么写才不踩坑
装饰器不是万能的,它只管“有没有”,不管“能不能”。常见错误是把业务判断(比如“只能删自己发的 post”)也塞进去,结果变成黑盒逻辑,调试困难。
立即学习“Python免费学习笔记(深入)”;
- 装饰器参数必须是字符串,比如
@require_permission("post:delete"),别传函数或对象,序列化和比对都麻烦 - 内部只做
if perm not in g.permissions: abort(403),绝不调用数据库或业务方法 - 如果需要组合权限(如
"post:delete"或"post:delete_others"),用any()判断,别写死多个or - 别在装饰器里处理未登录跳转——那是
@login_required的事,职责要分开
为什么基于路径的权限控制不如基于操作的控制可靠
有人用 request.path.startswith("/admin") 当权限依据,这非常脆弱。URL 可以重写、代理可改头、前端还能伪造请求——真正该校验的是“这个用户此刻是否被授权执行这个操作”。
- 比如
POST /api/posts/123是删除动作,权限应检查"post:delete",而不是看路径含不含"delete" - 同一个 endpoint 可能承载多个操作(GET 列表、POST 新建、PUT 更新),靠 method + path 组合判断更准
- 权限字符串建议统一用
resource:action格式(如"comment:approve"),方便未来按资源维度批量授权 - 前端按钮显隐可以复用同一套权限字符串,但绝不能只靠前端隐藏就认为安全——后端必须重复校验
RBAC 的复杂点不在建模,而在权限变更后的实时生效和多层级继承(比如角色继承)。先跑通四表+装饰器+操作级校验,再考虑 role hierarchy 或 context-aware permission(比如“编辑自己创建的文章”),别一上来就堆功能。


















