Casbin 的 Enforce() 必须在 Gin 中间件中调用,因 handler 执行时 c.Request.URL.Path 可能被重写,而中间件阶段 c.FullPath() 和 c.Request.Method 稳定可靠;obj 需用 c.FullPath()(去 query、去尾斜杠),act 需映射为语义化动作(如 GET→read),且模型必须用 keyMatch3 和 regexMatch。

直接说结论:Casbin 的 Enforce() 调用必须在 Gin 中间件里完成,且请求的 obj 和 act 必须从路由路径与 HTTP 方法中提取,不能硬编码或依赖上下文字段拼接。
为什么 Gin 中间件里调 Enforce() 才有效
很多人把 Casbin 权限校验写在 handler 里,结果发现权限没生效、或报错 panic: runtime error: invalid memory address。根本原因是:Gin 的 c.Request.URL.Path 在 handler 执行时可能已被重写(比如用了 gin.Recovery() 或其他中间件),而 Casbin 匹配依赖原始路径;同时,Enforce() 需要实时拿到当前请求的主体(role)、资源(path)、动作(method),这些只有在中间件阶段才能稳定获取。
- 中间件能确保在路由匹配后、handler 执行前介入,此时
c.FullPath()和c.Request.Method是确定值 - 若在 handler 里调用,用户可能已修改
c.Request,或因 panic 导致上下文丢失 - Casbin 的
enforcer.Enforce()是无状态函数,但输入三元组必须准确——sub应来自 JWT 解析后的角色(如"admin"),不是用户 ID
keyMatch3 和 regexMatch 在 RESTful 场景下的实际差异
RESTful 接口路径常含变量(如 /api/v1/users/:id),用 == 完全匹配会失败。Casbin 默认模型里的 keyMatch(r.obj, p.obj) 只支持 /foo/* 这类简单通配,对 Gin 的 :id 无效;必须升级为 keyMatch3。
-
keyMatch3("/api/v1/users/123", "/api/v1/users/:id")→true,它能识别 Gin 的路径参数占位符 -
regexMatch("GET", "GET|POST")→true,适合把多个 method 合并在一条策略里,比如p, admin, /api/v1/orders, GET|DELETE - 模型文件里必须显式写
keyMatch3(r.obj, p.obj)和regexMatch(r.act, p.act),不能只写keyMatch - 注意:
keyMatch3不处理 query 参数,/users?status=active和/users被视为不同obj
Gin 路由分组 + Casbin 策略怎么对齐才不漏权限
常见错误是把所有路由注册完再统一加中间件,结果 c.FullPath() 返回空字符串或 /。Gin 的 Fullpath() 只在匹配到具体路由后才有值,所以中间件必须注册在 group 层级,且策略里的 obj 必须和 Fullpath() 输出一致(不含 query,不带 trailing slash)。
- 注册方式:用
v1 := r.Group("/api/v1"),然后v1.Use(Authorize()),而不是r.Use(Authorize()) - 策略中的
obj应写成/api/v1/users,不是/users或/api/v1/users/ - 如果用了
gin.BasicAuth()或 JWT 中间件,确保它们在 Casbin 中间件之前执行,否则c.Get("role")拿不到值 - 测试时用
curl -X GET http://localhost:8080/api/v1/users,别带尾部斜杠,否则Fullpath()是/api/v1/users/,策略就得同步加斜杠
数据库加载策略时 JDBCAdapter 的字段顺序陷阱
用 MySQL 存策略时,Casbin 默认要求表字段顺序为 ptype, v0, v1, v2,但建表语句如果按 id, ptype, v0, v1, v2 定义,JDBCAdapter 会把 id 当作 ptype,导致 Enforce() 总返回 false。
- MySQL 表结构必须严格对应:
CREATE TABLE casbin_rule (ptype VARCHAR(100), v0 VARCHAR(100), v1 VARCHAR(100), v2 VARCHAR(100)); - 不要加
id主键字段,Casbin 不需要自增 ID;如果非要加,得自己实现LoadPolicyLine并跳过第一列 - 用
gormadapter.NewAdapterByDBUseTableName(db, "casbin_rule")加载时,确认db.Migrator().CurrentDatabase()返回的是目标库名,否则会连错库 - 调试时可临时用
enforcer.LoadPolicy()后打印enforcer.GetPolicy(),看加载进来的策略是否和 CSV 文件一致
最易被忽略的一点:Casbin 的 Enforce() 返回 false 不等于“拒绝”,它只表示“未匹配到允许策略”——如果你没配 deny 规则,且 policy_effect 是 some(where (p.eft == allow)),那没匹配就是默认拒绝。但一旦策略里混入 p, alice, /api/v1/users, POST 和 p, alice, /api/v1/users, *,后者会覆盖前者,导致权限扩大。路径越短、通配越宽,优先级越高,这点必须手写测试用例验证。


















