应直接用 Casbin 实现 RBAC,避免自写权限中间件;因其已内置角色继承、动态更新、多级资源匹配等能力,而 Beego Filter 自研 AuthFilter 存在递归查库、无缓存、热更新难、路径匹配不准等问题。

Beego 里做 RBAC,别自己从零写权限中间件 —— 直接用 Casbin,否则容易在角色继承、策略动态更新、多级资源匹配上反复踩坑。
为什么不用 Beego 自带 Filter 写 AuthFilter
很多人看到 Beego 的 Filter 机制就立刻动手写 AuthFilter,提取 ctx.Request.URL.Path 和 ctx.Request.Method 后查数据库比对权限。这看似可控,但很快会遇到几个硬伤:
- 角色嵌套(比如“管理员”属于“超级角色”)无法自动推导,得手动递归查
USER_ROLE→ROLE_PERMISSION→ROLE_ROLE - 每次请求都查库,没缓存时 QPS 上千就拖慢整个服务
- 权限变更后必须重启或手动清空内存策略,做不到热更新
- URL 路径带参数(如
/user/:id)或通配(/api/v1/*)时,字符串匹配极易漏判或误判
这些不是逻辑写不出来的“小问题”,而是 RBAC 模型本身的复杂性决定的。Casbin 的 enforce() 方法底层已处理好规则解析、匹配优先级、继承链展开,直接复用更稳。
如何把 Casbin 集成进 Beego 生命周期
Casbin 不是 Beego 插件,它本身无框架耦合,集成关键在于两点:初始化时机 + 中间件挂载点。常见错误是把 enforcer 声明在 controller 里,导致每次请求都新建实例。
- 在
main.go的init()或beego.Run()前完成初始化:e := casbin.NewEnforcer("rbac_model.conf", adapter),并全局变量保存(如var Enforcer *casbin.Enforcer) - 用 Beego 的
InsertFilter挂载,位置选beego.BEFORE_ROUTER,确保在路由匹配前拦截 - 中间件里不要重复查用户角色:Beego session 已有
uid,直接传给e.Enforce(uid, ctx.Request.URL.Path, ctx.Request.Method) - 注意路径标准化:Casbin 默认不处理 trailing slash,
/user和/user/被视为不同资源,建议统一用strings.TrimRight(ctx.Request.URL.Path, "/")
数据库表设计要不要建 admin_role 关联表
很多教程让 admin_info.role_ids 存逗号分隔字符串(如 "1,2"),说是“工程取舍”。实际项目跑半年后就会发现这招扛不住:
- MySQL 无法对逗号字段建高效索引,查“拥有角色 2 的所有用户”只能全表扫描
- 并发修改时易出现脏写:A 用户添加角色 3,B 用户同时删除角色 1,
UPDATE SET role_ids = REPLACE(role_ids, "1,", "")可能覆盖 A 的写入 - Casbin 的
AddRoleForUser()和DeleteRoleForUser()接口要求原子操作,依赖单条记录增删
老老实实建 admin_role 表,主键为 (admin_id, role_id) 复合唯一索引。虽然多一次 JOIN,但换来的是数据一致性、可审计性和 Casbin 策略同步的可靠性。
Casbin 策略更新时最常漏掉的一步
给用户改角色或改权限,只调 e.AddRoleForUser() 或 e.AddPermissionForUser() 是不够的。Casbin 的 RBAC 模型默认不自动清理旧策略 —— 它认为你可能想保留历史策略用于审计。
- 更新用户角色前,必须先执行
e.DeleteRolesForUser(uid),否则旧角色权限仍生效 - 更新某角色对某个资源的权限时,不能只
AddPermissionForRole(),得先e.DeletePermissionForRole(role, path, method)清空该资源下所有旧动作 - 如果用了自定义适配器(如 beego-orm-adapter),注意
Delete*方法是否真正触发了数据库 DELETE,有些适配器只删内存没删库
RBAC 的“角色”和“权限”本身是静态配置,但“谁属于哪个角色”“哪个角色能访问什么”这两层关联才是动态业务逻辑。Casbin 把策略存在内存里图的是快,但业务侧必须自己管好“增删改”的原子性,这点没法偷懒。


















