ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Yao OAuth ACL 权限执行引擎深度解析:五层校验链与数据访问约束实战指南

Yao OAuth ACL 权限执行引擎深度解析:五层校验链与数据访问约束实战指南 Agent 框架后端低代码RAG【免费下载链接】yao✨ All your agents and workspaces in one place, on every device you own. Track tasks on a board, accessible from desktop, mobile, browser, or API. Self-hosted.项目地址https://gitcode.com/gh_mirrors/ya/yao点击查看免费下载导读本文围绕 Yao 开源仓库中 openapi/oauth/acl/README.md 所定义的 ACLAccess Control List执行逻辑系统讲解 OAuth 受保护 API 的权限校验机制如何通过Client客户端、Token Scope令牌范围、Team团队、Member团队成员、User用户五个层次逐级校验请求以及校验通过后如何将数据访问约束自动注入请求上下文指导业务层做行级数据过滤。读者学完后将能独立理解 Yao 的 ACL 执行链源码enforce.go配置 scopes.yml / alias.yml / scope 定义文件并在自己的 API 处理器中正确读取authInfo.Constraints实现 Owner/Team/自定义维度的数据隔离。一、ACL 系统概述与核心原则ACLAccess Control List执行系统为 OAuth 受保护的 API 提供了一套完整的权限校验机制。它通过Client → Token Scope → Team → Member → User多个层次对请求进行验证任何一层失败都会立即拒绝访问并返回带有明确stage标记的错误便于快速定位失败环节。核心原则AND 逻辑所有适用的校验步骤必须全部通过AND 逻辑。只要任何一个检查失败访问立即被拒绝并返回指明失败阶段的具体错误。从源码结构看这一系统由三大部分构成见 openapi/oauth/acl 目录acl.goACL 入口与全局实例负责装配 ScopeManager、FeatureManager 与 RoleManagerenforce.go五层执行链enforceClient/enforceScope/enforceTeam/enforceMember/enforceUser的具体实现scope.goScopeManager 的配置加载、索引构建与正向/反向校验Check/CheckRestrictedrole/role.goRoleManager负责从缓存与数据源获取角色及其 scopes。二、执行流程总览ACL 的完整执行流程可用下面的流程图概括继承自原文档在入口ACL.Enforce(c *gin.Context)enforce.go中还有三个前置判断需要留意ACL 未启用!acl.Enabled()→ 直接放行return true, nilScopeManager 未加载acl.Scope nil→ 直接拒绝return false, nilPathPrefix 剥离若配置了acl.Config.PathPrefix例如/v1会先将请求路径中的该前缀剥离后再进行匹配这允许网关侧挂载统一前缀而不影响 scope 的路径规则。校验链最终返回(allowed, endpointInfo, err)全部通过时返回true并携带匹配到的endpointInfo若校验失败则返回带Stage标记的*acl.Error。三、五层校验详解3.1 Step 1Client 校验enforceClient目的验证发起请求的 OAuth 客户端是否有权访问该端点。过程源码见 enforce.go通过role.RoleManager.GetClientRole(ctx, clientID)获取客户端角色通过RoleManager.GetScopes(ctx, clientRole)获取角色 scopes返回allowedScopes与restrictedScopes正向检查用allowedScopes构造AccessRequest调用Scope.Check(request)失败立即拒绝Stage: client反向检查若存在 restrictedScopes用restrictedScopes构造AccessRequest调用Scope.CheckRestricted(request)若端点命中受限范围则立即拒绝Stage: client。结果✅通过两检查均通过进入 Step 2❌失败任一检查失败立即以Stage: client拒绝。值得注意的两个源码细节客户端角色兜底在 role/role.go 中若clientProvider未配置、或客户端未注册ErrorInvalidClient、或客户端没有role字段会回退到client:free角色——此时不施加客户端级限制后续的 Token Scope 与 User/Team 检查仍然生效角色未找到的降级GetScopes返回包含role not found的错误时enforceClient会记录警告并跳过客户端限制return true, nil, nil而不是直接拒绝从而避免因角色数据缺失导致整个 API 不可用见 enforce.go。3.2 Step 2Token Scope 校验enforceScope目的验证 OAuth 令牌中显式授予的 scopes。过程源码见 enforce.go检查authInfo.Scope是否为空为空则跳过本步骤继续 Step 3解析令牌 scopes以空格分隔的字符串例如read:users write:users→[read:users, write:users]用解析出的 scopes 构造AccessRequest调用Scope.Check(request)。结果⏭️跳过无令牌 scopes继续 Step 3✅通过继续 Step 3❌失败立即以Stage: scope拒绝错误details中会携带required_scopes与missing_scopes方便客户端/开发者判断缺失的权限。3.3 Step 3用户 / 团队校验按登录类型分支校验路径取决于AuthorizedInfo中的登录类型3.3.1 团队登录有TeamID3.3.1.1 团队权限校验enforceTeamenforce.goRoleManager.GetTeamRole(ctx, teamID)获取团队角色RoleManager.GetScopes(ctx, teamRole)获取allowedScopes与restrictedScopes正向检查Scope.Check(allowedScopes)失败 →Stage: team反向检查Scope.CheckRestricted(restrictedScopes)命中受限范围 →Stage: team。3.3.1.2 成员权限校验enforceMemberenforce.goRoleManager.GetMemberRole(ctx, teamID, userID)获取该用户在此团队内的角色同样获取 scopes 并做正向/反向双重校验任一失败 →Stage: member。结果团队登录必须同时通过enforceTeam与enforceMember两级校验才允许访问。团队校验管的是团队整体权限成员校验管的是该用户在团队内的个人角色权限二者是不同维度的约束。3.3.2 用户登录有UserID、无TeamID用户权限校验enforceUserenforce.goRoleManager.GetUserRole(ctx, userID)获取用户角色RoleManager.GetScopes(ctx, userRole)获取 scopes正向/反向双重校验任一失败 →Stage: user。3.3.3 纯 API 调用无UserID适用于 client credentials 授权或服务到服务调用仅需 Step 1Client 校验客户端校验通过即放行。若令牌 scope 也为空源码会记录一条警告日志提示此时 Step 1 的客户端权限是唯一的访问控制手段见 enforce.go。四、执行阶段Stages与错误处理4.1 执行阶段常量每个校验失败都会携带一个具体的stage标签用于调试与错误上报常量定义见 types.goStageConstantDescriptionClientEnforcementStageClientClient permission check failedScopeEnforcementStageScopeToken scope check failedTeamEnforcementStageTeamTeam permission check failedMemberEnforcementStageMemberTeam member permission check failedUserEnforcementStageUserUser permission check failed4.2 错误结构校验失败时返回的Error类型定义如下errors.gotype Error struct { Type ErrorType Message string Details map[string]interface{} RetryAfter int // seconds to wait before retrying (for rate limit errors) Stage EnforcementStage // stage where the permission check failed }其中ErrorType包含permission_denied、insufficient_scope、internal_error、rate_limit_exceeded、quota_exceeded等取值errors.go并实现了error接口Error()方法与IsRetryable()方法rate limit / quota 类错误返回 true。示例错误响应权限不足场景{ error: permission_denied, message: Access denied: insufficient permissions, stage: member, details: { required_scopes: [collections:write], missing_scopes: [collections:write] } }4.3 在 HTTP 处理器中处理 ACL 错误allowed, err : acl.Enforce(c) if err ! nil { aclErr : err.(*acl.Error) c.JSON(403, gin.H{ error: aclErr.Type, message: aclErr.Message, stage: aclErr.Stage, details: aclErr.Details, }) return }五、关键组件深入5.1 RoleManager角色与 scopes 的获取RoleManagerrole/role.go负责检索各实体的角色及其关联 scopesAPI 一览// Get role for different entities RoleManager.GetClientRole(ctx, clientID) - roleID RoleManager.GetUserRole(ctx, userID) - roleID RoleManager.GetTeamRole(ctx, teamID) - roleID RoleManager.GetMemberRole(ctx, teamID, userID) - roleID // Get scopes for a role RoleManager.GetScopes(ctx, roleID) - (allowedScopes, restrictedScopes, error)实现要点缓存优先每个Get*Role/GetScopes方法都先查缓存、未命中再查数据源、最后回写缓存见 role/role.go数据来源用户/团队/成员角色来自UserProviderGetUserRole/GetTeam/GetMember从返回结果中提取role_id字段客户端角色来自ClientProviderGetClientByIDscopes 拆分GetScopes从GetRolePermissions返回的权限数据中提取permissions正向与restricted_permissions反向分别格式化为allowedScopes与restrictedScopes见 role/role.go全局实例role.RoleManager是包级全局变量由 ACL 初始化时装配acl.go。5.2 ScopeManager端点访问校验ScopeManagerscope.go提供两种校验方法Check(request)—— 正向校验检查给定 scopes 是否授予访问该端点的权限type AccessRequest struct { Method string // HTTP method (GET, POST, etc.) Path string // Request path Scopes []string // Users scopes } decision : ScopeManager.Check(request) // Returns: AccessDecision with Allowed, Reason, MissingScopes, etc. // Allowed true: User has required scopes // Allowed false: User lacks required scopesCheck的内部逻辑scope.go若请求命中publicPaths公开端点→ 直接放行matchEndpoint按exact → param → wildcard三级优先级查找匹配端点scope.go未匹配到端点时按defaultAction默认deny决策按端点Policy分流PolicyAllow放行、PolicyDeny拒绝、PolicyRequireScopes则检查 scopesOR 关系命中任一必需 scope 即通过并记录MatchedScope。CheckRestricted(request)—— 反向校验负向检查检查给定 scopes 是否限制访问该端点decision : ScopeManager.CheckRestricted(request) // Returns: AccessDecision with Allowed, Reason, etc. // Allowed true: Endpoint is NOT restricted by these scopes // Allowed false: Endpoint IS restricted by these scopes (deny access)受限 scopes 的工作原理若端点命中restrictedScopes中任何一个 scope访问被拒绝限制优先于授权——即使allowedScopes已授予访问权restrictedScopes仍可将其拦截这实现了细粒度控制用户可以访问绝大多数端点但某些特定端点除外。示例// Role has: allowedScopes [collections:*, documents:*] restrictedScopes [collections:delete] // Request: DELETE /api/collections/123 // Check(allowedScopes) → Pass (collections:* matches) // CheckRestricted(restrictedScopes) → Fail (collections:delete matches) // Final result: Access DENIED匹配性能设计PathMatcher使用三级数据结构exactPaths→paramPaths→wildcardPaths通配路径按前缀长度降序排序以保证最长前缀优先匹配scope.go、scope.go路径匹配前会经normalizePath去除尾部斜杠避免/kb/teams/与/kb/teams匹配不一致scope.go。通配符 scope 匹配matchesWildcardScope支持*:*:*、resource:*:*、resource:action:*等模式要求通配模式与目标 scope 的分段数一致且不支持post*:read:all这类部分通配scope.go。5.3 配置加载与热更新LoadScopesscope.go按如下顺序装配 ScopeManager内置 scopes通过acl.Register(...)在代码中注册的内置 scope 定义优先载入全局配置加载openapi/scopes/scopes.ymldefault/public/endpoints别名配置加载openapi/scopes/alias.yml递归展开别名并检测循环引用scope 定义文件遍历openapi/scopes/下各资源子目录中的*.yml文件定义的同名 scope 覆盖内置定义构建运行时索引将全部规则落入endpointIndex/scopeIndex/aliasIndex。ScopeManager.Reload()支持整体重载配置scope.goRoleManager.ClearCache()用于清理角色缓存二者配合可实现配置变更后的在线生效。六、数据访问约束Data Access Constraints6.1 约束如何注入上下文ACL 校验通过后系统会从匹配到的端点提取数据访问约束并自动更新到请求上下文中enforce.goif endpointInfo ! nil { constraints : endpointInfo.GetConstraints() authorized.UpdateConstraints(c, constraints) }EndpointInfo.GetConstraints()types.go将内置约束与Extra自定义约束合并为一个map[string]interface{}API 处理器通过authorized.GetInfo(c)读取authInfo.Constraints即可获取。约束结构type DataConstraints struct { OwnerOnly bool // Only access owners data (current owner) CreatorOnly bool // Only access creators data (who created the resource) EditorOnly bool // Only access editors data (who last updated the resource) TeamOnly bool // Only access teams data (filter by TeamID) Extra map[string]interface{} // Custom constraints like department_only, region_only, etc. }6.2 约束来源Scope 定义中的标志位约束由 scope 定义文件中的owner/creator/editor/team/extra字段生成。系统在构建端点索引时会把这些标志合并进EndpointInfoscope.go若同一端点被多个 scope 引用约束按 OR 逻辑合并任一 scope 要求即置 true。此外GetScopeConstraints还可按实际命中的具体 scope 返回其原始约束避免多个 scope 合并带来的语义混淆scope.go。示例端点配置# openapi/scopes/collections/read.yml collections:read:own: name: collections:read:own description: Read own collections owner: true # Sets OwnerOnly true creator: true # Sets CreatorOnly true editor: true # Sets EditorOnly true extra: # Sets Extra constraints department_only: true region: us-west endpoints: - GET /api/collections/own - GET /api/collections/own/:id6.3 在 API 处理器中应用约束func GetCollections(c *gin.Context) { authInfo : authorized.GetInfo(c) query : db.Query(SELECT * FROM collections) // Apply built-in data access constraints if authInfo.Constraints.OwnerOnly { query query.Where(user_id ?, authInfo.UserID) } else if authInfo.Constraints.CreatorOnly { query query.Where(created_by ?, authInfo.UserID) } else if authInfo.Constraints.EditorOnly { query query.Where(updated_by ?, authInfo.UserID) } else if authInfo.Constraints.TeamOnly { query query.Where(team_id ?, authInfo.TeamID) } // Apply extra constraints if dept, ok : authInfo.Constraints.Extra[department_only].(bool); ok dept { query query.Where(department_id ?, authInfo.DepartmentID) } if region, ok : authInfo.Constraints.Extra[region].(string); ok { query query.Where(region ?, region) } // Execute query and return results collections, _ : query.Get() c.JSON(200, collections) }6.4 约束的可扩展性约束系统采用 map 驱动的方式天然支持自定义扩展内置约束OwnerOnly/CreatorOnly/EditorOnly/TeamOnly覆盖了最常见的行级过滤场景Extra 约束如department_only、region、project_ids可直接在 scope 的 YAML 中定义、在处理器中用类型断言读取无需改动任何核心代码仅当需要新增系统级高频内置约束时才需要仿照OwnerOnly等字段在代码中扩展对大多数场景使用Extra更灵活。三种内置约束的语义区别FAQ 官方口径OwnerOnly按当前所有者过滤谁现在拥有它可被转移CreatorOnly按原始创建者过滤谁创建了它不可变EditorOnly按最后编辑者过滤谁最后更新了它每次编辑都会变化。注意约束只是建议性的元数据真正的行级过滤必须由 API 处理器在查询中落实。切勿仅依赖 URL 路径/own、/team做访问控制。七、检查执行阶段Stage的典型用法allowed, err : acl.Enforce(c) if err ! nil { aclErr : err.(*acl.Error) switch aclErr.Stage { case acl.EnforcementStageClient: // Client doesnt have permission log.Error(Client permission denied, client_id, authInfo.ClientID) case acl.EnforcementStageUser: // User doesnt have permission log.Error(User permission denied, user_id, authInfo.UserID) case acl.EnforcementStageMember: // Team member doesnt have permission log.Error(Member permission denied, user_id, authInfo.UserID, team_id, authInfo.TeamID) } return }八、启用与配置8.1 启用 ACLconfig : acl.Config{ Enabled: true, Cache: cacheStore, Provider: userProvider, } aclInstance : acl.New(config)Config的完整字段types.go字段说明Enabled是否启用 ACL默认false见DefaultConfigPathPrefix网关挂载的 BaseURL 前缀如/v1校验前会从请求路径中剥离Cache角色缓存存储store.Store用于缓存角色与 scopes 查询结果ProviderUserProvider提供用户/团队/成员角色与权限数据ClientProviderClientProvider提供客户端角色数据getClientRole依赖acl.Load(config)在New基础上还会清空角色缓存保证加载后数据新鲜acl.go并将实例赋给全局acl.Global。8.2 RoleManager 初始化roleManager : role.NewManager(cacheStore, userProvider) role.RoleManager roleManager // Set global instanceNewManager(cache, provider, clientProvider)的三个参数与Config中的Cache/Provider/ClientProvider一一对应role/role.go。8.3 Scope 配置体系配合使用完整的 scope 配置体系参见 SCOPES_CONFIGURATION.md核心要点openapi/scopes/ ├── scopes.yml # Global configuration and default policies ├── alias.yml # Scope aliases for simplified permission management └── resource/ # Resource-specific scope definitions ├── collections.yml └── ...scopes.yml中default: deny默认拒绝显式放行是推荐的安全基线public列表声明免认证端点endpoints定义默认规则支持GET /kb/* allow字符串格式与{method, path, action}结构格式scope 定义遵循resource:action:level三段命名规范如collections:read:ownalias.yml支持将多个 scope 聚合为语义化别名如kb:read、system:root→*:*:*别名可嵌套引用并自动递归展开scope.go。九、设计原则纵深防御Defense in Depth多层独立校验确保整体安全性失败安全Fail-Safe任何一次校验失败都会导致访问被拒绝阶段明确Explicit Stages清晰的错误消息精确指出校验失败的环节独立校验Independent Validation每一阶段独立针对同一端点进行校验基于角色Role-Based权限通过角色与 scopes 管理双重校验Dual Validation每个阶段同时执行正向allowed与反向restricted检查——授权 scopes 必须授予端点访问权受限 scopes 必须不命中端点两个条件同时满足才放行限制优先Restriction Priority受限 scopes 覆盖授权 scopes实现细粒度控制。十、性能考量缓存RoleManager对角色与 scopes 查询进行缓存减少数据源压力提前退出Early Exit校验链在首次失败时立即终止不再执行后续阶段并发安全ScopeManager使用sync.RWMutex保护索引读多写少Check/CheckRestricted走读锁路径高效匹配PathMatcher按 exact → param → wildcard 三级优化结构匹配通配路径按前缀长度排序索引化查询scope 与别名在加载期即构建scopeIndex/aliasIndex/endpointIndex运行时索引请求期 O(1) 命中scope.go。十一、FAQ官方口径Q: RoleManager 未配置会发生什么A: ACL 启用时会自动初始化 RoleManager。若角色或权限检索失败如角色未找到执行链会返回带相应 stage 信息的错误。出于性能考虑启用 ACL 时应始终正确配置 RoleManager。Q: 一个实体可以有多个角色吗A: 目前每个实体client/user/team/member只有一个角色。多 scope 通过角色配置实现。Q: Team 校验与 Member 校验有何区别A: Team 校验检查团队的整体权限Member 校验检查该用户在该团队内的具体角色权限。Q: scope 匹配区分大小写吗A: 区分。例如read:users≠Read:Users。Q: 想要 OR 逻辑而不是 AND 逻辑怎么办A: 当前设计出于安全考虑使用 AND 逻辑。如需 OR可考虑给客户端或用户角色授予覆盖全部所需权限的 scopes。Q: restrictedScopes 到底如何工作A: 受限 scopes 采用反向校验Check(allowedScopes)问这些 scopes 是否授予访问权CheckRestricted(restrictedScopes)问这些 scopes 是否禁止访问。端点命中任一受限 scope 即拒绝与授权 scopes 无关。Q: 何时使用受限 scopesA需要授予宽泛访问权但屏蔽特定操作如允许所有 collections 操作但禁止删除、在不改动基础角色的前提下临时撤销某些端点访问、或实现通用权限的例外时。Q: 数据约束如何生效A: ACL 校验通过后系统检查匹配端点的 scope 定义中是否有约束标志owner/creator/editor/team/extra自动写入AuthorizedInfo.Constraints处理器从authorized.GetInfo(c)读取并应用数据过滤。Q: 多个约束可以同时为 true 吗A: 可以。一个 scope 可以同时设置多个约束。处理器应根据用例选择最严格或最合适的约束来应用过滤。Q: 约束已设置但用户上下文缺失怎么办A: 对无用户上下文的 client credentials 授权处理器应优雅处理如返回空结果或适当的错误。十二、进一步阅读README.md — ACL 执行逻辑本文主体来源DESIGN.md — ACL 系统整体设计SCOPES_CONFIGURATION.md — scope 配置完整指南目录结构、命名规范、通配符、最佳实践FEATURES_CONFIGURATION.md — 特性开关Features配置与 scopes 分工协作控制前端 UI 可见性enforce.go — 五层执行链源码scope.go — ScopeManager 校验与匹配源码role/role.go — RoleManager 角色获取与缓存源码types.go — Config / EndpointInfo / EnforcementStage 等核心结构errors.go — ACL 错误类型与构造方法理解并善用这套 ACL 执行链你就能在自托管的 Yao 平台上构建出既具备纵深防御、又支持行级数据隔离的 OAuth API 权限体系。赞分享Agent 框架后端低代码RAG【免费下载链接】yao✨ All your agents and workspaces in one place, on every device you own. Track tasks on a board, accessible from desktop, mobile, browser, or API. Self-hosted.项目地址https://gitcode.com/gh_mirrors/ya/yao点击查看免费下载相关推荐fhEVM 智能合约 ACL 实战指南密文授权、访问校验与用户解密权限管理fhEVM 智能合约 ACL 实战指南密文授权、访问校验与用户解密权限管理 本文基于 fhEVMFully Homomorphic Encryption E密码学隐私计算区块链后端Yao OAuth ACL 权限系统设计解析从 PathMatcher 索引到 O(1) 权限判定Yao OAuth ACL 权限系统设计解析从 PathMatcher 索引到 O 1 权限判定 本篇文章围绕 Yao 仓库 openapi/oauth/acAgent 框架后端低代码RAGYao 项目 OAuth ACL Scope 配置完全指南从权限模型到 YAML 实战Yao 项目 OAuth ACL Scope 配置完全指南从权限模型到 YAML 实战 本指南以 Yao 开源仓库中 openapi/oauth/acl/ 目Agent 框架后端低代码RAG创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表