Kratos 不开箱即用支持 OpenFGA,因其鉴权设计解耦留白,仅提供接口抽象而无默认实现;集成需在业务层手动调用 OpenFGA SDK 或 API 构造三元组并校验,且模型、元组、超时等须严格配置。

Kratos 框架本身不内置权限引擎,集成 OpenFGA 是可行的,但必须手动在业务拦截层(如 middleware 或 service 层)调用 OpenFGA 的 SDK 或 HTTP API,不能靠 Kratos 自动注入或声明式配置完成。
为什么 Kratos 不像 OPA 那样“开箱即用”支持 OpenFGA
Kratos 的鉴权设计是解耦但留白的:它提供 authz.Middleware 接口和 authz.Authorizer 抽象,但默认实现只做 JWT 解析与简单角色比对;它不绑定任何外部授权服务。OpenFGA 是独立部署的服务,需你主动发起 Check 请求,Kratos 不会自动帮你构造 user:alice、relation:editor、object:document:123 这类元组结构。
常见误判是以为加个配置就能启用 ReBAC —— 实际上 Kratos 的 authz.Config 里根本没有 OpenFGA 相关字段,强行写进去会被忽略。
在 Kratos service 层调用 OpenFGA check 的实操要点
推荐在具体业务 service 方法内部(而非全局 middleware)做细粒度校验,因为只有这里才明确知道当前操作涉及的 user、object 和 relation 三元组:
- 从
ctx中提取用户 ID(比如从 JWT payload 的sub字段),不要依赖中间件挂载的模糊字段(如ctx.Value("user")可能是 struct,无法直接用于 OpenFGA) - 构造 OpenFGA 的
CheckRequest时,user必须是合法主体格式,例如"user:alice"或"group:engineering#member",不能只传"alice" -
object必须带类型前缀,例如"document:123",且该类型需已在 OpenFGA 模型中定义(如type document) -
relation必须与模型中定义的 relation 名完全一致,大小写敏感,例如模型里写的是can_view,就不能传view - 建议设置超时(如
context.WithTimeout(ctx, 500*time.Millisecond)),避免 OpenFGA 服务抖动拖垮整个接口
Kratos + OpenFGA 容易踩的坑
实际部署中最常导致 check 返回 false 却查不出原因的几个点:
- OpenFGA 模型未生效:改完模型后忘了调用
WriteAuthorizationModel,或者用错了store_id,新模型写到了另一个 store - 关系元组写错类型:比如把
user:alice写成alice,OpenFGA 会静默忽略该元组,ReadChanges也查不到——因为它根本没入库 - Kratos 的
http.Request中丢失原始请求路径参数,导致 service 层拿不到document_id,从而无法构造object字符串 - 本地开发用内存 store(
memory)测试通过,上线切 PostgreSQL 后忘记初始化 schema,check直接 panic 或返回 500 - 没处理 OpenFGA 的
NotFound错误(HTTP 404),误当成权限拒绝,其实只是 store 或 model 不存在
要不要在 Kratos middleware 里统一封装 OpenFGA 调用
可以封装,但别试图“通用化”。比如写一个 openfga.AuthzMiddleware,要求每个 handler 提前在 ctx 里塞入 openfga.Object 和 openfga.Relation —— 这看似省事,实则让业务逻辑污染了传输层,违反 Kratos “清晰分层”原则。
更务实的做法是:在 service 层按用例写明确定义的检查函数,例如:
func (s *Service) CanViewDocument(ctx context.Context, userID string, docID string) (bool, error) {
resp, err := s.openfgaClient.Check(ctx, &openfga.CheckRequest{
User: "user:" + userID,
Relation: "can_view",
Object: "document:" + docID,
})
// ...
}
这样每处调用都可读、可测、可单独打日志,出问题时一眼定位到是哪个文档、哪个用户、哪个 relation 出了问题。
真正复杂的地方不在代码怎么写,而在于 OpenFGA 模型设计是否覆盖了所有隐含关系(比如“部门主管能审批下属提交的合同”,这种间接关系需要靠 computed_userset 或 tuple_to_userset 表达),以及元组写入时机是否与业务状态变更严格同步——这两点没法靠 Kratos 或 SDK 自动解决。


















