4. 权限模型设计:RBAC、ABAC 模型对比与网关抽象

在权限审计网关中,权限模型是决定“谁(Who)能对什么(What)执行何种操作(How)”的核心逻辑。本章深入对比两种主流模型——RBAC(基于角色的访问控制)与ABAC(基于属性的访问控制),并阐述如何在网关层进行统一的抽象表示。

4.1 RBAC 模型:角色驱动的静态权限

RBAC 通过“用户 → 角色 → 权限”的映射关系实现访问控制。其核心思想是将权限授予角色,再将角色授予用户,从而简化权限管理。

要素 说明 网关中的抽象
用户(User) 访问系统的实体(如 API 调用方) JWT 中的 sub 或 user_id 声明
角色(Role) 权限的集合(如 admin、editor、viewer) JWT 中的 roles 声明(字符串数组)
权限(Permission) 对资源的操作许可(如 article:write) 网关策略引擎中的规则键
会话(Session) 用户与角色的动态绑定关系 请求上下文中的 ctx.Roles 字段
网关实践要点:RBAC 适用于权限层级固定、角色数量有限的场景。在网关层,通常将角色列表解析到请求上下文中,策略引擎通过预加载的“角色-权限”映射表进行快速匹配。

4.2 ABAC 模型:属性驱动的动态权限

ABAC 基于主体(Subject)、资源(Resource)、动作(Action)和环境(Environment)四类属性进行动态评估。其粒度更细,能够处理复杂的上下文敏感场景。

属性类别 示例 网关中的抽象
主体属性 用户 ID、部门、安全等级、地理位置 JWT 声明 + 请求上下文扩展字段
资源属性 资源类型、所属项目、数据分类、创建时间 请求路径、请求头、资源元数据缓存
动作属性 HTTP 方法(GET/POST/PUT/DELETE)、操作类型 请求方法 + 路由参数
环境属性 当前时间、IP 段、网络类型、客户端版本 请求来源 IP、时间戳、User-Agent
性能考量:ABAC 每次请求都需要评估一组属性表达式,对网关性能有直接影响。建议使用预编译的策略表达式(如 CEL、Rego)并启用属性缓存,避免重复解析。

4.3 RBAC vs ABAC 核心对比

对比维度 RBAC ABAC
权限粒度 粗粒度(角色级别) 细粒度(属性级别)
管理复杂度 低,角色数量可控 高,属性与策略数量易膨胀
动态性 静态,需管理员手动变更角色 动态,属性变化自动影响权限
适用场景 企业内部系统、CMS、后台管理 多租户 SaaS、金融风控、数据脱敏
网关实现成本 低,查表即可 高,需集成策略引擎
审计可追溯性 容易,角色变更记录清晰 较复杂,需记录属性快照

4.4 网关中的统一抽象表示

为了兼顾 RBAC 的简洁性与 ABAC 的灵活性,权限审计网关通常采用分层抽象架构:底层支持 RBAC 快速过滤,上层通过 ABAC 策略引擎进行精细化裁决。

网关权限模型分层抽象架构 API 请求入口 身份与角色解析层 JWT 解析 → 提取 sub / roles / 属性声明 RBAC 快速过滤层 角色 → 权限映射表匹配(O(1) 查表) ABAC 策略引擎 属性表达式评估(CEL / Rego) RBAC 直接放行 / 拒绝 命中角色权限表,无需 ABAC 评估 裁决输出(允许/拒绝) 说明:RBAC 层作为第一道过滤器,未命中角色权限的请求才进入 ABAC 引擎进行属性级评估,兼顾性能与灵活性。

4.5 Go 语言中的抽象实现

在网关代码中,我们通过接口抽象统一两种模型。以下是一个精简的权限评估器接口定义:

// PermissionEvaluator 权限评估器接口
type PermissionEvaluator interface {
    // Evaluate 评估请求是否具有指定权限
    // 返回 true 表示允许,false 表示拒绝
    Evaluate(ctx context.Context, req *PermissionRequest) (bool, error)
}

// PermissionRequest 权限请求上下文
type PermissionRequest struct {
    UserID    string                 // 用户 ID
    Roles     []string               // 用户角色列表(RBAC 使用)
    Action    string                 // 请求动作(如 "read", "write")
    Resource  string                 // 资源标识(如 "article:123")
    Attributes map[string]interface{} // 扩展属性(ABAC 使用)
    Env       map[string]interface{} // 环境属性(ABAC 使用)
}

// RBACEvaluator RBAC 实现
type RBACEvaluator struct {
    rolePerms map[string]map[string]bool // role -> action:resource -> allow
}

func (e *RBACEvaluator) Evaluate(ctx context.Context, req *PermissionRequest) (bool, error) {
    key := req.Action + ":" + req.Resource
    for _, role := range req.Roles {
        if perms, ok := e.rolePerms[role]; ok {
            if allowed, ok := perms[key]; ok {
                return allowed, nil
            }
        }
    }
    return false, nil // 默认拒绝
}

// ABACEvaluator ABAC 实现(使用 CEL 引擎示例)
type ABACEvaluator struct {
    policies []*Policy
}

func (e *ABACEvaluator) Evaluate(ctx context.Context, req *PermissionRequest) (bool, error) {
    for _, p := range e.policies {
        if p.Matches(req) {
            return p.Effect == EffectAllow, nil
        }
    }
    return false, nil
}
架构决策:在网关启动时,将 RBAC 映射表加载到内存中(如 sync.Map),ABAC 策略编译为可执行的 AST 缓存。请求到达时,先执行 RBAC 查表,若未命中则调用 ABAC 引擎。这种“先粗后细”的策略将 80% 的请求拦截在 RBAC 层,显著降低 ABAC 引擎的调用频率。

4.6 模型选择决策树

在实际网关设计中,可根据以下条件快速选择主导模型:

  1. 角色数量是否 < 50? → 是:优先 RBAC;否:考虑 ABAC
  2. 权限变更频率是否 > 每天 10 次? → 是:ABAC 更灵活;否:RBAC 更稳定
  3. 是否需要根据时间、IP 等环境因素控制访问? → 是:必须引入 ABAC;否:RBAC 足够
  4. 审计日志是否需要记录每次决策的详细属性? → 是:ABAC 天然支持;否:RBAC 可简化
  5. 团队是否有运维能力维护策略 DSL? → 是:可上 ABAC;否:建议 RBAC + 少量 ABAC 扩展
推荐实践:大多数权限审计网关采用“RBAC 主体 + ABAC 扩展”的混合模式。即:用 RBAC 管理 90% 的通用权限,对敏感操作(如删除、导出、跨部门访问)叠加 ABAC 属性规则。这样既保持了核心模型的简洁,又保留了应对复杂场景的弹性。