Skip to content

分析yao openapi oauth角色acl中的实现原理

结合 yao/openapi 目录下的源码,深入浅出地剖析 Yao 框架中的 OAuth、角色(Role)与 ACL(访问控制列表) 的实现机制。

可以将这套系统想象成一个高级办公大楼的门禁系统

  • OAuth: 是发卡处(验证你是谁,给你发门禁卡)。
  • Role (角色): 是你的职位(经理、清洁工、访客)。
  • ACL: 是那本厚厚的规则书(经理可以进金库,访客只能在大厅)。
  • Permissions (权限/Scope): 是具体的动作(“开门”、“查看文件”)。

1. 核心原理:门禁是如何工作的?

在代码层面,整个流程发生在 openapi/oauth 目录中。核心逻辑链条如下:

  1. 请求拦截 (Guard):

    • 代码位置: openapi/oauth/guard.go
    • 原理: 这是一个“保安”。每个进入 Yao 系统接口的 HTTP 请求,都要先过这一关。它会检查请求头里的 Authorization: Bearer <Token>
    • 源码逻辑: Guard() 函数解析 Token,提取出当前用户的 ID 和 Role (角色)
  2. 权限仲裁 (Enforce):

    • 代码位置: openapi/oauth/acl/enforce.go
    • 原理: 保安拿着你的角色,去查阅“规则书”。
    • 核心函数: Enforce(role, resource, method)。它判断:拥有这个 role 的人,能否对这个 resource(API路径)执行 method(GET/POST等)操作。
  3. 规则定义 (ACL & Scopes):

    • 代码位置: openapi/oauth/acl/acl.goscope.go
    • 原理: 这里定义了具体的规则。Yao 采用的是 基于 Scope(作用域)的控制。角色是一组 Scope 的集合。

2. 配置:规则书长什么样?

Yao 的权限配置通常不是写死的代码,而是通过 DSL (JSON/YAML) 配置加载的。虽然源码中展示了处理逻辑,但实际使用中,你会在应用配置中看到类似结构。

根据 openapi/oauth/acl 目录下的逻辑,配置主要分为三部分:

2.1 定义资源与 Scope (Scopes Configuration)

系统首先定义了哪些动作需要什么权限标签(Scope)。

  • 资源: 例如 /api/v1/chat
  • 动作: POST
  • 所需 Scope: chat.create

2.2 定义角色 (Role Configuration)

然后,定义“角色”拥有哪些“Scope”。

  • Admin (管理员): 拥有 * (所有权限) 或 chat.*, user.*
  • User (普通用户): 仅拥有 chat.create, user.profile.read

2.3 代码中的默认加载

openapi/oauth/acl/acl.go 中,通常会有默认的加载逻辑。Yao 可能会从文件系统加载 acl.yao 或类似的配置文件,将其解析为内存中的 Go 结构体。


3. 使用方式:如何授权与控制

这是初学者最关心的部分:“我该怎么把一个用户变成管理员,并让他能访问 API?”

3.1 给用户授权角色 (Assign Role)

在 Yao 中,用户的角色通常存储在数据库的用户表中,或者在登录时动态计算。

  • 源码线索: 查看 openapi/oauth/providers/user/ 目录。
  • 实现方式:
    1. 数据库层面: 你的 user 表中通常有一个 role 字段(例如 JSON 类型或字符串)。
    2. 登录颁发令牌: 当用户调用登录接口(如 openapi/user/login.go)时,系统验证密码成功。
    3. 生成 Token: 系统调用 openapi/oauth/token.go 中的 New() 方法。
      • 关键点: 在生成 JWT Token 时,会将数据库中查到的 role 字段的值,写入 Token 的 payload 中。
      • 结果: 用户拿到的 Token 里就刻上了 "我是 Admin" 的印记。

操作示例 (伪代码):

javascript
// 在数据库中更新用户
Process('models.user.save', {
  id: 1,
  name: '张三',
  role: 'admin' // <--- 这里赋予角色
});

3.2 角色与具体权限关联 (Mapping)

这部分通常是在 Yao 的服务启动时通过 DSL 配置完成的,但源码中体现了其匹配逻辑。

原理 (Scope Match):openapi/oauth/acl/scope.go 中,有一个匹配算法。

  • 如果 API 需要 users.edit 权限。
  • 如果用户的角色是 editor,配置中 editor 拥有 ["users.*"]
  • ACL 会进行通配符匹配:users.* 包含 users.edit -> 允许通过

3.3 在 API 中使用 (Use Guard)

如果你自己在开发自定义的 API 接口(Go 或 DSL),你需要告诉系统“这个接口受保护”。

在 DSL (apis/user.http.json) 中:

json
{
  "paths": [
    {
      "path": "/info",
      "method": "GET",
      "guard": "bearer-jwt", // <--- 这里调用了 oauth.Guard
      "process": "models.user.find",
      "in": ["$session.user_id"]
    }
  ]
}

注意:DSL 中的 guard: "bearer-jwt" 底层就是映射到了 openapi/oauth/guard.go 的逻辑。


4. 总结:初学者流程图

如果你要实现“让小明拥有管理员权限”,你的脑海中应该有这幅图:

  1. 配置阶段: 确保 Yao 的 ACL 配置中定义了 admin 角色,并且 admin 角色拥有 * (所有) Scope。
  2. 数据阶段: 去数据库修改小明的记录,把他的 role 字段改为 admin
  3. 登录阶段: 小明重新登录。Yao 的登录逻辑读取数据库 -> 发现他是 admin -> 把 role: admin 封印进 JWT Token -> 发给小明。
  4. 请求阶段: 小明带着 Token 访问 /api/delete-db
  5. 验证阶段:
    • Guard 看到 Token,解密出 role: admin
    • Enforce 检查:/api/delete-db 需要 database.delete 权限。
    • ACL 判断:admin 拥有所有权限,当然包含 database.delete
    • 放行

5. 给开发者的建议

如果你想修改或扩展这部分功能:

  1. 调试: 可以在 openapi/oauth/guard.go 中加日志,打印出 c.Request().Header.Get("Authorization") 看看收到的 Token 对不对。
  2. 扩展: 如果你需要更复杂的权限(比如根据部门、根据具体 ID),你需要修改 openapi/oauth/acl/enforce.go,在标准的 Role 检查之后,加入自定义的逻辑(比如 ABAC 属性访问控制)。
  3. MCP: 注意到目录中有 openapi/mcpopenapi/oauth/mcp.go,这意味着 Yao 的 Agent/AI 接口也复用了这套鉴权逻辑,给 AI 赋予特定的“角色”和“Scope”是目前的高级用法。