3. 权限模型与 RBAC:权限三要素与 Go 结构体设计

权限控制是网关审计的基石。在分布式系统中,RBAC(基于角色的访问控制) 是最成熟、应用最广泛的权限模型。本章从权限三要素出发,逐步推导出 RBAC 的核心设计,并给出 Go 语言中的结构体定义与代码示例。

3.1 权限三要素:用户、角色、权限

任何权限系统都围绕三个核心概念展开:

要素 定义 示例
用户 (User) 访问系统的实体,可以是人、服务账号或设备 user:zhangsan、service:payment
角色 (Role) 权限的集合,是用户与权限之间的逻辑桥梁 admin、editor、viewer
权限 (Permission) 对某个资源执行特定操作的许可 order:create、report:export
核心原则:用户不直接关联权限,而是通过角色间接获得权限。这种解耦使得权限管理在规模增长时依然可控。

3.2 RBAC 模型设计

RBAC 模型定义了用户-角色-权限之间的多对多关系。标准 RBAC(NIST RBAC 核心模型)包含以下关系:

  • 用户分配 (UA):用户 ↔ 角色(多对多)
  • 权限分配 (PA):角色 ↔ 权限(多对多)
  • 角色继承(可选):角色可以继承其他角色的权限,形成层级结构

下图展示了 RBAC 核心模型的结构与数据流:

RBAC 核心模型结构图 RBAC 核心模型:用户 → 角色 → 权限 用户 (User) 角色 (Role) 权限 (Permission) UA 分配 PA 分配 角色继承(可选) 子角色继承父角色权限 权限判定流程 用户登录 → 获取用户角色列表 → 合并角色权限 → 缓存权限集合 → 请求鉴权 每次请求只需检查用户权限缓存,无需重复查询数据库

3.3 Go 中权限结构体定义

在 Go 项目中,我们通常使用结构体来映射 RBAC 的实体关系。以下是一组经过生产验证的结构体定义:

// User 用户实体
type User struct {
    ID        uint64    `json:"id" gorm:"primaryKey"`
    Username  string    `json:"username" gorm:"uniqueIndex;size:64"`
    Email     string    `json:"email" gorm:"size:128"`
    Status    int8      `json:"status"` // 1:启用 0:禁用
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
}

// Role 角色实体
type Role struct {
    ID          uint64    `json:"id" gorm:"primaryKey"`
    Name        string    `json:"name" gorm:"uniqueIndex;size:64"`
    Description string    `json:"description" gorm:"size:256"`
    ParentID    *uint64   `json:"parent_id"` // 父角色ID,支持角色继承
    CreatedAt   time.Time `json:"created_at"`
    UpdatedAt   time.Time `json:"updated_at"`
}

// Permission 权限实体
type Permission struct {
    ID          uint64    `json:"id" gorm:"primaryKey"`
    Resource    string    `json:"resource" gorm:"size:128"`   // 资源标识,如 "order"
    Action      string    `json:"action" gorm:"size:64"`      // 操作标识,如 "create"
    Description string    `json:"description" gorm:"size:256"`
    CreatedAt   time.Time `json:"created_at"`
}

// UserRole 用户-角色关联表
type UserRole struct {
    ID     uint64 `json:"id" gorm:"primaryKey"`
    UserID uint64 `json:"user_id" gorm:"index"`
    RoleID uint64 `json:"role_id" gorm:"index"`
}

// RolePermission 角色-权限关联表
type RolePermission struct {
    ID           uint64 `json:"id" gorm:"primaryKey"`
    RoleID       uint64 `json:"role_id" gorm:"index"`
    PermissionID uint64 `json:"permission_id" gorm:"index"`
}
设计要点:
  • 权限标识:使用 Resource:Action 格式(如 order:create),便于统一管理和通配符匹配。
  • 角色继承:ParentID 字段支持角色层级,查询时需递归或使用闭包表。
  • 关联表:UserRole 和 RolePermission 是典型的多对多中间表,索引字段确保查询性能。
  • 状态控制:用户和角色都应包含状态字段,支持软禁用而不删除数据。

3.4 权限缓存与判定函数

在实际网关中,每次请求都查询数据库是不现实的。以下是一个基于缓存的权限判定函数示例:

// PermissionCache 用户权限缓存(简化版)
type PermissionCache struct {
    mu          sync.RWMutex
    userPerms   map[uint64]map[string]struct{} // userID -> set of "resource:action"
}

// HasPermission 检查用户是否拥有指定权限
func (pc *PermissionCache) HasPermission(userID uint64, resource, action string) bool {
    pc.mu.RLock()
    defer pc.mu.RUnlock()

    perms, ok := pc.userPerms[userID]
    if !ok {
        return false
    }

    // 精确匹配
    key := resource + ":" + action
    if _, exists := perms[key]; exists {
        return true
    }

    // 通配符匹配:resource:*
    wildcardKey := resource + ":*"
    if _, exists := perms[wildcardKey]; exists {
        return true
    }

    // 超级管理员匹配:*:*
    if _, exists := perms["*:*"]; exists {
        return true
    }

    return false
}

// RefreshUserPerms 刷新指定用户的权限缓存
func (pc *PermissionCache) RefreshUserPerms(userID uint64, db *gorm.DB) {
    var perms []string
    // SQL: 通过用户ID查询所有关联权限
    db.Raw(`
        SELECT DISTINCT p.resource || ':' || p.action
        FROM permissions p
        JOIN role_permissions rp ON p.id = rp.permission_id
        JOIN user_roles ur ON rp.role_id = ur.role_id
        WHERE ur.user_id = ?
    `, userID).Scan(&perms)

    permSet := make(map[string]struct{}, len(perms))
    for _, p := range perms {
        permSet[p] = struct{}{}
    }

    pc.mu.Lock()
    pc.userPerms[userID] = permSet
    pc.mu.Unlock()
}
注意:上述 SQL 查询使用了 PostgreSQL 的字符串拼接语法 ||。若使用 MySQL,请改用 CONCAT(p.resource, ':', p.action)。生产环境中建议将权限刷新操作放在异步队列中执行,避免阻塞主请求链路。

3.5 本章小结

知识点 关键内容
权限三要素 用户、角色、权限,三者通过 UA 和 PA 关系解耦
RBAC 模型 用户→角色→权限的间接授权体系,支持角色继承
Go 结构体 User、Role、Permission 实体 + UserRole、RolePermission 关联表
缓存策略 内存缓存 + 通配符匹配,减少数据库查询压力