RBAC需五张表:users、roles、permissions、user_roles、role_permissions;其中user_roles和role_permissions为关联表,须设联合主键与外键约束,权限校验推荐用permission_code而非ID以提升可维护性。

Fiber 框架本身不内置 RBAC,得自己搭结构 + 中间件拦截 + 数据库关联查询,否则所有路由都裸奔。
怎么设计 User-Role-Permission 三张表关系
必须用外键约束,别图省事搞字符串拼权限。Fiber 是无状态 HTTP 框架,权限判断全靠每次请求查数据库或缓存,所以表结构直接影响性能。
-
users表存id、username、password_hash -
roles表存id、name(如"admin"、"editor") -
permissions表存id、code(如"user:read"、"order:delete"),description -
user_roles是中间表:user_id+role_id -
role_permissions是中间表:role_id+permission_code(注意:这里推荐用permission_code而非permission_id,避免后期改权限名时连带更新外键)
怎么写 Fiber 的 RBAC 中间件
核心是拦截请求后,从 JWT 或 session 取 user_id,查出该用户所有 permission_code 集合,再比对当前路由所需的权限码。别在中间件里做完整 SQL JOIN,容易 N+1。
- 先用
user_id查user_roles.role_id,再用这批role_id批量查role_permissions.permission_code—— 两步 SQL,用IN和SELECT ... FROM ... WHERE role_id IN (?) - 把结果塞进
map[string]struct{},比如perms["user:write"] = struct{}{},判断时用if _, ok := perms["user:write"]; !ok { c.Status(403).SendString("forbidden") } - 权限码建议按
资源:动作命名,比如"product:update",别用模糊的"can_edit",否则后期无法精准控制 - 中间件注册时,把所需权限码作为参数传入:
app.Post("/products", requirePermission("product:create"), handler)
为什么不能直接用 Fiber 的 ctx.Locals 存权限列表
可以存,但容易误用。常见坑是:在中间件里查完权限后只往 ctx.Locals["permissions"] 塞一次,后续 handler 里反复读这个 map —— 看似没问题,但一旦你加了并发请求(比如前端发多个并行 API),而中间件没做并发安全处理,就可能读到脏数据或 panic。
- 更稳妥的做法是:查完立刻转成不可变结构,比如
map[string]bool或sync.Map,且只读不写 - 如果用了 Redis 缓存权限(推荐),注意设置合理过期时间(如 15 分钟),避免用户角色变更后权限延迟生效
- 别在
ctx.Locals存 DB 连接或事务对象,Fiber 不保证中间件执行顺序和生命周期,容易泄露连接
真正麻烦的不是写中间件,而是权限变更时如何让已签发的 JWT 失效 —— Fiber 不管这事,得你自己结合 Redis 黑名单或缩短 token 过期时间来兜底。











