第3章 核心数据模型设计:五表结构与字段设计
权限系统的数据模型是RBAC落地的基石。本章从边界出发,围绕分层、组件职责、数据流/控制流/信任链路、选型权衡、风险点、落地步骤和验收标准,详细阐述用户表、角色表、权限表、用户‑角色关联表、角色‑权限关联表的完整设计。
边界声明:本章设计的五表模型仅覆盖RBAC0核心部分;RBAC1(角色继承)、RBAC2(约束)和RBAC3(层次+约束)将在后续章节扩展。
3.1 边界与分层
五表模型是权限系统的数据存储层,不包含业务逻辑、缓存策略或授权判定算法。其上层为策略层(如Spring Security Filter / Azul),下层为持久化层(如MySQL、PostgreSQL)。
| 层 | 职责 | 不做什么 |
|---|---|---|
| 数据存储层(五表) | 存储用户/角色/权限及关系 | 不执行权限判断、不校验权限完整性 |
| 策略层 | 加载权限数据、执行判定 | 不直接访问表(通过DAO/Repository) |
| 业务层 | 编排、缓存、审计 | 不直接修改权限关系(通过API) |
3.2 组件职责与字段设计
3.2.1 用户表 (users)
核心身份标识容器。建议字段:
CREATE TABLE users (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(64) NOT NULL UNIQUE,
password VARCHAR(256) NOT NULL, -- 加密哈希
email VARCHAR(128),
status TINYINT NOT NULL DEFAULT 1, -- 0:禁用 1:启用
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
3.2.2 角色表 (roles)
权限聚合单元,面向组织岗位设计。
CREATE TABLE roles (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(64) NOT NULL UNIQUE,
description VARCHAR(256),
is_system TINYINT NOT NULL DEFAULT 0, -- 1:系统内置角色(不可删除)
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
3.2.3 权限表 (permissions)
原子权限,描述对某个资源的某种操作(+效果)。
CREATE TABLE permissions (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
resource VARCHAR(128) NOT NULL, -- 例如: "order"
action VARCHAR(64) NOT NULL, -- 例如: "create","delete"
effect ENUM('allow','deny') NOT NULL DEFAULT 'allow',
description VARCHAR(256),
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_resource_action (resource, action, effect)
);
字段权衡:使用字符串而非数字枚举便于可读性,但索引长度需控制。生产环境推荐将resource/action转为字典表或枚举类型以减少存储。
3.2.4 用户-角色关联表 (user_roles)
多对多关系,一个用户可对应多个角色。
CREATE TABLE user_roles (
user_id BIGINT UNSIGNED NOT NULL,
role_id BIGINT UNSIGNED NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (user_id, role_id)
);
3.2.5 角色-权限关联表 (role_permissions)
角色与权限的多对多映射。
CREATE TABLE role_permissions (
role_id BIGINT UNSIGNED NOT NULL,
permission_id BIGINT UNSIGNED NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (role_id, permission_id)
);
3.3 数据流 / 控制流 / 信任链路
- 数据流:用户登录 → 查询用户表 → 关联user_roles → 查询角色表 → 关联role_permissions → 获取权限列表并缓存至策略层。
- 控制流:权限判定请求 → 策略层读取缓存或重新查表 → 检查user_roles和role_permissions → 返回允许/拒绝。
- 信任链路:数据库层确保ACID;应用层使用事务保证关联操作的原子性;缓存层需考虑写穿透(Write‑Through)避免脏数据。
3.4 选型权衡
| 决策点 | 方案A | 方案B | 推荐场景 |
|---|---|---|---|
| 主键类型 | 自增BIGINT | UUID | 分库分表用UUID,单库用自增 |
| 关联表是否带额外字段 | 仅PK | 加valid_from/valid_to做时间范围 | 临时权限/偏移需求选方案B |
| 权限效果(allow/deny) | 仅allow | 支持deny | 需要显式拒绝时选方案B |
| 角色是否可继承 | 不分层(RBAC0) | 增加parent_role_id(RBAC1) | 大型组织选方案B |
风险点:
- 联合主键(user_id, role_id)若userId为非有序UUID,会导致索引页分裂严重;建议搭配雪花ID或自增。
- 关联表未建立外键约束(性能损耗),导致业务层需额外校验数据一致性。
- 权限表使用字符串枚举时,资源重命名成本高——需要同时修改所有引用该权限的角色。
3.5 落地步骤
- 确定业务域(如订单、用户、商品)并划分权限粒度。
- 按照上述DDL执行五表创建(先不建外键,后续根据性能权衡加索引)。
- 初始化系统权限数据(insert into permissions)。
- 创建默认角色(例如管理员、普通用户)并关联权限。
- 编写DAO层(MyBatis / JPA)并集成单元测试。
- 集成权限查询服务,验证完整数据流。
3.6 验收标准
| 验收项 | 预期结果 | 验证方法 |
|---|---|---|
| 用户表主键唯一 | 插入相同username失败 | 执行 INSERT 测试 |
| 角色名唯一 | 插入重复name报错 | 执行 INSERT 测试 |
| 权限唯一约束 | 相同resource+action+effect只允许一个 | 执行 INSERT 测试 |
| 用户-角色关联完整性 | 删除用户/角色时级联删除关联记录(可选) | 执行DELETE测试 |
| 权限查询性能 | 1000万条user_roles记录,单用户查询 < 5ms | EXPLAIN + 压测 |
下一阶段准备:第4章将基于此模型实现权限判定引擎,包括缓存策略、级联判断与deny覆盖逻辑。请确保数据库脚本已在开发环境就绪。