4. 权限模型与RBAC设计:用户-角色-权限关系、资源与操作定义、Go结构体建模与数据库表设计

在构建权限审计网关时,权限模型是核心基石。本章采用业界成熟的基于角色的访问控制(RBAC)模型,将用户与权限解耦,通过角色作为中间层,实现灵活、可扩展的权限管理。我们将从概念模型、资源与操作定义、Go结构体建模到数据库表设计,逐层深入。

4.1 RBAC核心概念与关系

RBAC模型包含三个核心实体:用户(User)、角色(Role)、权限(Permission)。其核心思想是:权限不直接授予用户,而是授予角色;用户通过扮演角色获得权限。这种设计极大简化了权限管理,尤其适用于企业级多用户、多资源的系统。

RBAC 核心实体关系图 用户 (User) 角色 (Role) 权限 (Permission) 多对多 多对多 核心原则 • 用户通过角色间接获得权限,而非直接关联。 • 一个用户可拥有多个角色,一个角色可被多个用户拥有。 • 一个角色可包含多个权限,一个权限可被多个角色包含。

4.2 资源与操作定义

权限的本质是对资源(Resource)执行操作(Action)的许可。在网关场景中,资源通常是API端点或服务,操作则是HTTP方法或自定义动作。

概念 说明 示例
资源 (Resource) 被访问的对象,可以是API路径、服务名、数据实体 /api/v1/orders、user-service、report:finance
操作 (Action) 对资源执行的动作,通常对应CRUD或自定义操作 read、write、delete、export
权限标识 (Permission Key) 资源与操作的组合,唯一标识一个权限点 order:read、user:write、report:export
设计建议:权限标识采用 资源:操作 的命名规范,便于阅读和匹配。例如 order:read 表示“读取订单”权限。在网关层,通过解析请求的路径和方法,可以快速映射到对应的权限标识。

4.3 Go结构体建模

在Go语言中,我们使用结构体来映射RBAC实体。以下代码展示了核心模型的定义,包括字段标签、JSON序列化支持以及必要的关联关系。

// User 用户模型
type User struct {
    ID        uint      `gorm:"primaryKey" json:"id"`
    Username  string    `gorm:"uniqueIndex;size:64;not null" json:"username"`
    Password  string    `gorm:"size:256;not null" json:"-"`
    Email     string    `gorm:"size:128" json:"email"`
    Status    int       `gorm:"default:1" json:"status"` // 1:启用 0:禁用
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
    // 关联角色(多对多)
    Roles     []Role    `gorm:"many2many:user_roles;" json:"roles,omitempty"`
}

// Role 角色模型
type Role struct {
    ID          uint         `gorm:"primaryKey" json:"id"`
    Name        string       `gorm:"uniqueIndex;size:64;not null" json:"name"`
    Description string       `gorm:"size:256" json:"description"`
    IsSystem    bool         `gorm:"default:false" json:"is_system"` // 系统内置角色不可删除
    CreatedAt   time.Time    `json:"created_at"`
    UpdatedAt   time.Time    `json:"updated_at"`
    // 关联权限(多对多)
    Permissions []Permission `gorm:"many2many:role_permissions;" json:"permissions,omitempty"`
}

// Permission 权限模型
type Permission struct {
    ID          uint      `gorm:"primaryKey" json:"id"`
    Resource    string    `gorm:"size:128;not null" json:"resource"`     // 资源标识,如 "order"
    Action      string    `gorm:"size:64;not null" json:"action"`       // 操作标识,如 "read"
    Description string    `gorm:"size:256" json:"description"`
    CreatedAt   time.Time `json:"created_at"`
    UpdatedAt   time.Time `json:"updated_at"`
}

// UserRole 用户-角色关联表(GORM自动处理many2many时可选显式定义)
type UserRole struct {
    UserID uint `gorm:"primaryKey"`
    RoleID uint `gorm:"primaryKey"`
}

// RolePermission 角色-权限关联表
type RolePermission struct {
    RoleID       uint `gorm:"primaryKey"`
    PermissionID uint `gorm:"primaryKey"`
}
建模要点:
  • 使用 gorm:"many2many:..." 标签声明多对多关系,GORM会自动维护中间表。
  • 密码字段标记为 json:"-",避免在JSON序列化时泄露敏感信息。
  • IsSystem 字段用于保护系统内置角色,防止误删除。
  • 权限模型采用 Resource + Action 组合,而非直接存储字符串标识,便于后续扩展和查询。

4.4 数据库表设计

基于Go结构体,我们设计对应的MySQL数据库表。以下是核心表的DDL语句,包含索引和约束。

-- 用户表
CREATE TABLE `users` (
    `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    `username` VARCHAR(64) NOT NULL COMMENT '用户名',
    `password` VARCHAR(256) NOT NULL COMMENT '密码(加密后)',
    `email` VARCHAR(128) DEFAULT '' COMMENT '邮箱',
    `status` TINYINT DEFAULT 1 COMMENT '状态: 1启用 0禁用',
    `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
    `updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
    PRIMARY KEY (`id`),
    UNIQUE INDEX `idx_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

-- 角色表
CREATE TABLE `roles` (
    `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(64) NOT NULL COMMENT '角色名称',
    `description` VARCHAR(256) DEFAULT '' COMMENT '角色描述',
    `is_system` TINYINT(1) DEFAULT 0 COMMENT '是否系统内置',
    `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
    `updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
    PRIMARY KEY (`id`),
    UNIQUE INDEX `idx_role_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表';

-- 权限表
CREATE TABLE `permissions` (
    `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    `resource` VARCHAR(128) NOT NULL COMMENT '资源标识',
    `action` VARCHAR(64) NOT NULL COMMENT '操作标识',
    `description` VARCHAR(256) DEFAULT '' COMMENT '权限描述',
    `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
    `updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
    PRIMARY KEY (`id`),
    UNIQUE INDEX `idx_resource_action` (`resource`, `action`),
    INDEX `idx_resource` (`resource`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='权限表';

-- 用户-角色关联表
CREATE TABLE `user_roles` (
    `user_id` BIGINT UNSIGNED NOT NULL,
    `role_id` BIGINT UNSIGNED NOT NULL,
    PRIMARY KEY (`user_id`, `role_id`),
    INDEX `idx_role_id` (`role_id`),
    CONSTRAINT `fk_user_roles_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
    CONSTRAINT `fk_user_roles_role` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户角色关联表';

-- 角色-权限关联表
CREATE TABLE `role_permissions` (
    `role_id` BIGINT UNSIGNED NOT NULL,
    `permission_id` BIGINT UNSIGNED NOT NULL,
    PRIMARY KEY (`role_id`, `permission_id`),
    INDEX `idx_permission_id` (`permission_id`),
    CONSTRAINT `fk_role_permissions_role` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE,
    CONSTRAINT `fk_role_permissions_permission` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色权限关联表';
性能与安全注意事项:
  • 关联表使用复合主键 (user_id, role_id) 和 (role_id, permission_id),避免重复记录。
  • 外键约束使用 ON DELETE CASCADE,删除用户或角色时自动清理关联关系,防止数据孤岛。
  • 权限表的 (resource, action) 联合唯一索引,确保同一个资源操作组合不会重复定义。
  • 生产环境中建议对 user_roles 和 role_permissions 表定期进行碎片整理,维持查询性能。

4.5 权限缓存策略预览

在网关层面,每次请求都查询数据库进行权限校验会带来较大延迟。因此,我们需要引入缓存层。下图展示了权限数据在缓存中的存储结构,为后续章节的缓存一致性方案奠定基础。

权限缓存存储结构(Redis) 用户角色缓存 Key: user:roles:{userID} Value: [role1, role2, ...] 角色权限缓存 Key: role:perms:{roleID} Value: [perm1, perm2, ...] 权限详情缓存 Key: perm:info:{permID} Value: {resource, action} 缓存设计目标 • 减少数据库查询,将权限校验延迟控制在毫秒级。 • 当角色或权限变更时,通过版本号或消息队列实现缓存失效,保证最终一致性。