4. RBAC 数据库设计:用户、角色、权限与关联表

聊完了 RBAC 的模型概念,咱们得落地了。说白了,就是怎么设计几张数据库表,把用户、角色、权限这几个东西存起来,还能高效地查。这事我做过挺多次,每次重构权限系统,核心表结构其实都大差不差,但细节上稍不注意,后面就可能踩坑。

4.1 核心表:三张基础表

最基本的 RBAC 设计,需要三张实体表,外加两张关联表。我先说说这三张实体表。

  • 用户表(user):存谁在用系统。字段太简单?嗯,但状态字段你得多留个心眼。
  • 角色表(role):存有哪些岗位或身份。比如“管理员”、“运营编辑”、“普通用户”。
  • 权限表(permission):存具体的操作许可。比如“创建文章”、“删除用户”、“查看报表”。

我见过一些设计,把权限和菜单混在一起,这其实不好。权限最好只抽象为“操作”,而不是“页面url”。页面只是操作的载体。你想想看,一个页面可能有多个权限点(增、删、改),所以权限表最好只描述“能做什么”。

4.2 用户表(user)

我个人习惯,用户表里不直接放角色字段。角色应该通过关联表去映射,这样一个人就能有多个角色了。

避坑提醒: 我曾经接手过一个老项目,用户表里有个 role_id 字段,只能存一个角色。后来业务要求“一个人既是审核员又是管理员”,结果被迫改表结构,改了一大堆业务代码。所以,一开始就设计成多对多,后面会省心很多。
字段名类型说明
idbigint主键
usernamevarchar(50)登录名,唯一
passwordvarchar(255)加密后的密码
statustinyint0-禁用,1-启用
created_atdatetime创建时间
状态字段建议用 tinyint,别用 varchar。查询时 int 比字符串快,而且 0/1 的语义很清晰。

4.3 角色表(role)

角色是权限的集合。一个角色对应一整套操作能力。表结构很简单,但 role_code 字段我建议加上,因为代码里经常要用这个 code 做逻辑判断,而不是靠 id 或 name。

字段名类型说明
idbigint主键
role_codevarchar(50)角色编码,唯一,如 ADMIN
role_namevarchar(50)角色名称,如 管理员
descriptionvarchar(200)角色描述
我记得有一次,需求方想批量给“所有运营角色”加个权限,我们直接根据 role_code like 'OPS%' 就查出来了。要是直接用 id,你还得去另一张表里查关联,麻烦不少。

4.4 权限表(permission)

权限表的设计,这里有个分歧。有人喜欢把权限做成树形结构(比如菜单权限、按钮权限分开),但基础 RBAC 里,我倾向于先扁平化。每个权限就是一个“原子操作”。

字段名类型说明
idbigint主键
perm_codevarchar(50)权限编码,如 article:create
perm_namevarchar(50)权限名称,如 创建文章
modulevarchar(50)所属模块,如 article
为什么不直接用 id 关联?因为代码里写死 id 的话,换环境(测试、预发)时 id 可能不一样,但 code 可以约定为常量。这也是我在项目里吃过亏才总结的。

4.5 关联表:打通用户与权限

实体表之间是多对多的关系。一个用户可以有多个角色,一个角色可以有多个权限。所以,需要两张关联表来桥接它们。

  • 用户-角色关联表(user_role)
  • 角色-权限关联表(role_permission)

为什么会这样设计?因为用户和权限之间如果直接关联,系统会变得非常灵活,但也会非常复杂。RBAC 的核心思想就是通过“角色”这一中间层来管理,这样日常维护时,你只需要给角色加减权限,用户会自动继承。

关联表字段示例
user_roleuser_id, role_id用户 1001 关联角色 2(管理员)
role_permissionrole_id, perm_id角色 2 关联权限 5、6、7(增删改)
这两张表,我建议都加上联合唯一索引。比如 UNIQUE KEY uk_user_role (user_id, role_id),防止重复关联。这是基本功,但很多人会忘。

4.6 完整的 SQL 实现示例

下面给你一个完整的 DDL 示例。你可以直接拿来用,或者根据需求微调。嗯,这里要注意字符集和引擎的选择,我个人习惯用 InnoDB + utf8mb4,兼容性好,支持事务。


-- 用户表
CREATE TABLE `user` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL,
  `password` varchar(255) NOT NULL,
  `status` tinyint NOT NULL DEFAULT '1',
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 角色表
CREATE TABLE `role` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `role_code` varchar(50) NOT NULL,
  `role_name` varchar(50) NOT NULL,
  `description` varchar(200) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_role_code` (`role_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 权限表
CREATE TABLE `permission` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `perm_code` varchar(50) NOT NULL,
  `perm_name` varchar(50) NOT NULL,
  `module` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_perm_code` (`perm_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 用户-角色关联表
CREATE TABLE `user_role` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `user_id` bigint NOT NULL,
  `role_id` bigint NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_user_role` (`user_id`, `role_id`),
  KEY `idx_role_id` (`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 角色-权限关联表
CREATE TABLE `role_permission` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `role_id` bigint NOT NULL,
  `perm_id` bigint NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_role_perm` (`role_id`, `perm_id`),
  KEY `idx_perm_id` (`perm_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
核心逻辑: 当你查询某个用户有哪些权限时,不需要直接查 permission 表。你只需要从 user -> user_role -> role -> role_permission -> permission 这条链路走一遍。如果数据量大,记得加合适的索引,别全表扫描。

4.7 一个小扩展:租户隔离

如果你是做 SaaS 系统的,可能会遇到多租户问题。这时候,每张表里再加一个 tenant_id 字段,所有查询都带上这个条件,就能做到数据隔离。我的做法是:把 tenant_id 作为联合索引的第一个字段,因为查询时基本都会带上。

举个例子,用户表可能变成这样:

CREATE TABLE `user` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `tenant_id` bigint NOT NULL,
  `username` varchar(50) NOT NULL,
  -- 其他字段略
  UNIQUE KEY `uk_tenant_username` (`tenant_id`, `username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
注意:租户隔离下,用户名可以跨租户重复,但在同一个租户内必须是唯一的。所以联合唯一索引很重要。

好了,表结构讲到这里。这是 RBAC 最经典、最实用的数据库设计。你可以在它的基础上自由扩展,比如加上数据权限字段、部门字段等等。基础打牢了,后面玩 ABAC 时才会更顺手。

RBAC 核心表结构及关系图 RBAC 核心数据库表关系 用户表 (user) id, username, status 角色表 (role) id, role_code, role_name 权限表 (permission) id, perm_code, perm_name 用户-角色关联表 (user_role) 角色-权限关联表 (role_permission) 查询链路:user → user_role → role → role_permission → permission 多对多关系通过中间关联表解耦 角色是用户和权限之间的桥梁