enforce() 总返回 false 的根本原因是模型、策略、参数三者未对齐;常见问题包括请求定义与传参顺序不一致、domain 模型缺参数、matcher 字段名不匹配、policy 或 role 定义缺失或格式错误,以及路径/subject/action 提取不规范。

Enforce() 总返回 false?先核对模型、策略、参数三者是否对齐
90% 的权限拦截失效,不是 Casbin 本身出错,而是 e.Enforce() 调用时模型定义、策略数据、传参顺序没对齐。Casbin 不报错,只静默返回 false。
-
[request_definition]写的是r = sub, obj, act,你就必须按e.Enforce("alice", "/api/users", "GET")传参——顺序错、少一个、多一个,都失败 - 中间件里从
c.Request提取字段时,常把c.Request.Method当act、c.Request.URL.Path当obj,但调用Enforce时却把obj和act位置写反 - 启用了 domain 模型(
r = sub, dom, obj, act),却只传三个参数 → 第四个dom缺失,匹配直接跳过 - 自定义
[matchers]里用了r.act,但模型中[request_definition]定义的是method字段名 → 表达式求值失败,不报错,只返回false
调试建议:启动时加 e.EnableLog(true),看日志里是否出现 no policy matched 或 matcher: false,比翻 policy.csv 快得多。
model.conf 缺一个区块就 panic,必须显式写全四部分
Casbin 启动时校验结构,不是运行时报错——它根本起不来。缺任何一块都会 panic,不是 warning。
-
[request_definition]必须声明Enforce()入参结构,例如r = sub, obj, act;写成r = user, path, method也行,但后续所有地方(策略、代码)必须严格一致 -
[policy_definition]必须有p = sub, obj, act(RBAC 基础格式),漏掉p.前缀或字段数不匹配,加载策略会失败 -
[role_definition]必须存在且为g = _, _;写成g = _, _, _却只传两个参数给AddRoleForUser(),角色继承会静默失效 -
[policy_effect]必须是合法表达式,如e = some(where (p.eft == allow));空着、语法错误、或写成e = allow都会导致初始化失败
漏掉 [role_definition]?所有 g 关系(比如 user:1 → role:admin)都不生效;[matchers] 里没写 keyMatch2(r.obj, p.obj) 却想匹配 /api/users/123 和 /api/users/*?通配完全不触发。
中间件里怎么安全提取 sub / obj / act?别硬编码路径前缀
常见错误是直接用 c.Request.URL.Path 当 obj,但实际路由可能带统一前缀(如 /v1 或 /api),而策略里存的是去前缀后的路径。
- 用
strings.TrimPrefix(c.Request.URL.Path, global.GVA_CONFIG.System.RouterPrefix)(如 gin-vue-admin)或手动配置的 base path 做清理,否则策略永远匹配不上 -
sub别直接用用户名字符串,推荐用AuthorityId或用户 ID(如strconv.Itoa(int(claims.AuthorityId))),避免角色名变更导致策略失效 -
act一般直接取c.Request.Method,但注意大小写:策略里存GET,就别传get;若需统一小写,中间件里先strings.ToUpper() - 如果业务需要区分「查看详情」和「列表查询」,仅靠 HTTP 方法不够,得在路径或 query 中带标识,再拼进
obj(如/api/users/detail),否则策略粒度太粗
路径处理不一致,是最隐蔽的权限放行漏洞来源——表面一切正常,但某条策略始终不命中。
上线别用 policy.csv,GORM Adapter 加载时机很关键
开发阶段用 fileadapter.NewAdapter("policy.csv") 没问题,但生产环境必须切数据库。不是因为“文件不安全”,而是多实例并发写 policy.csv 会导致策略丢失或解析失败(in 错误)。
- 推荐用
gormadapter.NewAdapterByDBUseTableName(db, "casbin_rule"),表结构由 Casbin 自动建好 -
enforcer.LoadPolicy()必须在服务启动完成、数据库连接就绪后执行;放在init()或 DB 初始化前,会加载空策略 - 策略变更后,别 reload 整个 model,用
enforcer.AddPolicy()/enforcer.RemovePolicy()动态增删更安全;reload 可能引发短暂权限真空 - 超级用户(如
root)要写进[matchers]:|| r.sub == "root",而不是靠策略行硬编码——这样改策略不用动模型
动态策略加载看似简单,但 LoadPolicy() 的时机、并发安全、以及是否清空旧策略,三者错一个,线上权限就乱套。











