Database design permission
平台权限系统的设计需以RBAC(基于角色的访问控制)为核心,并扩展数据级权限能力(解决 “能看什么数据” 的问题),同时兼顾功能级权限(解决 “能做什么操作” 的问题)。以下是包含数据库表设计、核心接口设计、权限校验逻辑的完整平台权限系统设计方案,适用于中大型企业级平台(如 SaaS、管理后台、多租户系统)。
一、核心权限模型选型
平台权限系统优先选择 RBAC3(RBAC 的扩展模型)+ 数据权限 的组合方案:
- RBAC3:支持角色继承、用户 - 权限直接关联、角色互斥,解决纯 RBAC 角色膨胀的问题;
- 数据权限:基于部门、租户、数据范围(全部 / 本人 / 本部门)实现精细化数据访问控制,是平台权限的核心补充。
整体权限关系链:用户 ←→ 角色 ←→ 功能权限 ←→ 资源,同时角色 ←→ 数据权限规则约束数据可见性。
二、数据库表设计
数据库表设计遵循第三范式,同时为性能做适度冗余,核心分为基础权限表、资源表、数据权限表和扩展表(多租户 / 日志)。以下以 MySQL 为例,给出表结构及核心字段说明。
1. 基础权限表
(1)用户表(sys_user)
存储平台用户基础信息,关联用户所属部门 / 租户。
| 字段名 | 类型 | 主键 / 索引 | 说明 | 示例值 |
|---|---|---|---|---|
| user_id | bigint | 主键 | 用户唯一标识 | 1001 |
| username | varchar(50) | 唯一索引 | 登录账号(不可重复) | admin |
| password | varchar(100) | 加密密码(MD5/SHA256 + 盐值) | xxx(加密后) | |
| real_name | varchar(50) | 用户真实姓名 | 张三 | |
| dept_id | bigint | 索引 | 所属部门 ID(关联部门表) | 201 |
| tenant_id | bigint | 索引 | 租户 ID(多租户平台必选) | 1 |
| status | tinyint | 状态(0 - 禁用,1 - 启用) | 1 | |
| create_time | datetime | 创建时间 | 2025-12-23 10:00:00 | |
| update_time | datetime | 更新时间 | 2025-12-23 11:00:00 |
(2)角色表(sys_role)
存储平台角色定义,角色是权限的集合。
| 字段名 | 类型 | 主键 / 索引 | 说明 | 示例值 |
|---|---|---|---|---|
| role_id | bigint | 主键 | 角色唯一标识 | 301 |
| role_name | varchar(50) | 角色名称(如超级管理员) | 系统管理员 | |
| role_code | varchar(50) | 唯一索引 | 角色编码(不可重复,用于权限判断) | ADMIN |
| role_desc | varchar(200) | 角色描述 | 拥有平台全部权限 | |
| parent_id | bigint | 索引 | 父角色 ID(支持角色继承) | 0(无父角色) |
| tenant_id | bigint | 索引 | 租户 ID(多租户隔离) | 1 |
| status | tinyint | 状态(0 - 禁用,1 - 启用) | 1 | |
| create_time | datetime | 创建时间 | 2025-12-23 10:00:00 |
(3)权限表(sys_permission)
存储功能级权限,绑定具体的资源(如接口、按钮、菜单)。
| 字段名 | 类型 | 主键 / 索引 | 说明 | 示例值 |
|---|---|---|---|---|
| perm_id | bigint | 主键 | 权限唯一标识 | 401 |
| perm_name | varchar(50) | 权限名称 | 用户管理 - 新增 | |
| perm_code | varchar(100) | 唯一索引 | 权限编码(核心,用于校验) | sys:user:add |
| resource_type | tinyint | 索引 | 资源类型(1 - 菜单,2 - 按钮,3 - 接口) | 2 |
| resource_id | bigint | 索引 | 关联资源表 ID(如菜单 ID / 接口 ID) | 501 |
| tenant_id | bigint | 索引 | 租户 ID | 1 |
| status | tinyint | 状态(0 - 禁用,1 - 启用) | 1 |
(4)用户 - 角色关联表(sys_user_role)
用户与角色的多对多关联表(一个用户可拥有多个角色)。
| 字段名 | 类型 | 主键 / 索引 | 说明 | 示例值 |
|---|---|---|---|---|
| user_role_id | bigint | 主键 | 关联 ID | 601 |
| user_id | bigint | 联合索引 | 用户 ID | 1001 |
| role_id | bigint | 联合索引 | 角色 ID | 301 |
| tenant_id | bigint | 索引 | 租户 ID | 1 |
(5)角色 - 权限关联表(sys_role_permission)
角色与权限的多对多关联表(一个角色可包含多个权限)。
| 字段名 | 类型 | 主键 / 索引 | 说明 | 示例值 |
|---|---|---|---|---|
| role_perm_id | bigint | 主键 | 关联 ID | 701 |
| role_id | bigint | 联合索引 | 角色 ID | 301 |
| perm_id | bigint | 联合索引 | 权限 ID | 401 |
| tenant_id | bigint | 索引 | 租户 ID | 1 |
2. 资源表(sys_resource)
存储平台的资源实体(菜单、按钮、接口),权限最终绑定到资源上。
| 字段名 | 类型 | 主键 / 索引 | 说明 | 示例值 |
|---|---|---|---|---|
| resource_id | bigint | 主键 | 资源唯一标识 | 501 |
| resource_name | varchar(50) | 资源名称 | 用户管理菜单 | |
| resource_type | tinyint | 索引 | 资源类型(1 - 菜单,2 - 按钮,3 - 接口) | 1 |
| path | varchar(200) | 资源路径(菜单路由 / 接口 URL) | /sys/user | |
| component | varchar(200) | 前端组件路径(仅菜单) | sys/user/List | |
| parent_id | bigint | 索引 | 父资源 ID(如子菜单归属父菜单) | 0(顶级菜单) |
| sort | int | 排序号(用于前端展示) | 10 | |
| status | tinyint | 状态(0 - 禁用,1 - 启用) | 1 |
3. 数据权限表(sys_data_permission)
存储角色的数据访问规则,解决 “能看什么数据” 的问题,是平台权限的核心扩展。
| 字段名 | 类型 | 主键 / 索引 | 说明 | 示例值 |
|---|---|---|---|---|
| data_perm_id | bigint | 主键 | 数据权限唯一标识 | 801 |
| role_id | bigint | 索引 | 关联角色 ID | 301 |
| resource_id | bigint | 索引 | 关联资源 ID(如用户管理接口) | 501 |
| data_scope | tinyint | 数据范围(1 - 全部,2 - 本人,3 - 本部门,4 - 指定部门) | 3 | |
| dept_ids | varchar(500) | 指定部门 ID 集合(data_scope=4 时生效) | 201,202 | |
| tenant_id | bigint | 索引 | 租户 ID | 1 |
4. 扩展表(可选)
- 部门表(sys_dept):存储组织架构,字段包括
dept_id、dept_name、parent_id、sort、tenant_id等; - 权限操作日志表(sys_perm_log):记录权限变更(如角色分配、权限修改),字段包括
log_id、oper_user、oper_type、oper_content、oper_time等; - 租户表(sys_tenant):多租户平台必选,字段包括
tenant_id、tenant_name、expire_time、status等。
三、核心接口设计
接口遵循RESTful 风格,统一响应格式为{ "code": 200, "msg": "成功", "data": {} },其中code为状态码(200 成功,403 权限拒绝,500 服务器错误)。
1. 角色管理接口
负责角色的增删改查、权限分配。
| 接口名称 | 请求方式 | 接口路径 | 请求参数示例 | 响应数据示例 |
|---|---|---|---|---|
| 创建角色 | POST | /api/v1/role | {"roleName": "运营专员", "roleCode": "OPERATOR", "roleDesc": "运营操作权限", "tenantId": 1} | {"code": 200, "msg": "创建成功", "data": { "roleId": 302} } |
| 删除角色 | DELETE | /api/v1/role/{roleId} | 路径参数:roleId=302 | {"code": 200, "msg": "删除成功"} |
| 修改角色 | PUT | /api/v1/role/{roleId} | {"roleName": "高级运营专员", "roleDesc": "高级运营操作权限"} | {"code": 200, "msg": "修改成功"} |
| 查询角色列表 | GET | /api/v1/role | 分页参数:page=1&size=10&tenantId=1 | { "code": 200, "data": { "list": [...], "total": 10 } } |
| 角色分配权限 | POST | /api/v1/role/{roleId}/perm | { "permIds": [401, 402, 403] } | {"code": 200, "msg": "权限分配成功"} |
| 查询角色权限 | GET | /api/v1/role/{roleId}/perm | 路径参数:roleId=301 | { "code": 200, "data": [ { "permId": 401, "permCode": "sys:user:add" }, ... ] } |
2. 用户权限接口
负责用户的角色绑定、权限查询。
| 接口名称 | 请求方式 | 接口路径 | 请求参数示例 | 响应数据示例 |
|---|---|---|---|---|
| 用户绑定角色 | POST | /api/v1/user/{userId}/role | { "roleIds": [301, 302] } | {"code": 200, "msg": "角色绑定成功"} |
| 解除用户角色 | DELETE | /api/v1/user/{userId}/role | { "roleIds": [302] } | {"code": 200, "msg": "角色解除成功"} |
| 查询用户权限 | GET | /api/v1/user/{userId}/perm | 路径参数:userId=1001 | { "code": 200, "data": { "permCodes": ["sys:user:add", "sys:role:list"] } } |
| 查询当前用户权限 | GET | /api/v1/user/current/perm | 无(从 Token 获取当前用户 ID) | { "code": 200, "data": { "permCodes": [...], "dataScope": 3 } } |
3. 权限资源接口
负责资源(菜单、按钮、接口)的管理。
| 接口名称 | 请求方式 | 接口路径 | 请求参数示例 | 响应数据示例 |
|---|---|---|---|---|
| 新增资源 | POST | /api/v1/resource | {"resourceName": "用户管理按钮", "resourceType": 2, "path": "/sys/user/add", "parentId": 501} | { "code": 200, "data": { "resourceId": 502 } } |
| 查询资源树 | GET | /api/v1/resource/tree | 参数:resourceType=1(仅查菜单) | { "code": 200, "data": [ { "resourceId": 501, "children": [...] }, ... ] } |
4. 数据权限接口
负责数据权限规则的配置与查询。
| 接口名称 | 请求方式 | 接口路径 | 请求参数示例 | 响应数据示例 |
|---|---|---|---|---|
| 配置数据权限 | POST | /api/v1/data-perm | { "roleId": 302, "resourceId": 501, "dataScope": 3, "deptIds": "201,202" } | {"code": 200, "msg": "配置成功"} |
| 查询角色数据权限 | GET | /api/v1/data-perm/role/{roleId} | 路径参数:roleId=302 | { "code": 200, "data": [ { "dataScope": 3, "deptIds": "201,202" }, ... ] } |
四、权限校验实现
权限系统的核心是 “校验”,需覆盖前端按钮 / 菜单和**后端接口 / 数据 ** 两层校验。
1. 前端权限校验
- 菜单权限:前端从
/api/v1/user/current/perm获取当前用户的资源列表,根据resource_type=1(菜单)过滤出可展示的菜单,渲染路由; -
按钮权限:通过权限编码(如
sys:user:add)判断按钮是否显示,示例(Vue 代码):<el-button v-if="hasPerm('sys:user:add')" type="primary">新增用户</el-button> <script> export default { methods: { hasPerm(permCode) { return this.userPerms.includes(permCode); // userPerms为当前用户的权限编码数组 } } } </script>
2. 后端权限校验
-
接口权限校验:通过拦截器 / AOP实现,步骤如下:
- 从 Token 中解析当前用户 ID,查询用户的权限编码列表;
- 解析接口对应的权限编码(如通过注解
@RequiresPerm("sys:user:add")); -
对比用户权限编码是否包含接口所需权限,不包含则返回 403。
示例(Spring Boot 拦截器核心代码):
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { // 获取当前用户权限 List<String> userPerms = SecurityUtils.getCurrentUserPerms(); // 获取接口所需权限 RequiresPerm annotation = ((HandlerMethod) handler).getMethodAnnotation(RequiresPerm.class); if (annotation != null && !userPerms.contains(annotation.value())) { response.setStatus(403); return false; } return true; } -
数据权限校验:通过SQL 拼接 / 数据过滤实现,步骤如下:
- 根据当前用户的角色,查询数据权限规则(
data_scope和dept_ids); - 拼接 SQL 条件,例如:
- 数据范围为 “本人”:
and create_user_id = #{userId}; - 数据范围为 “本部门”:
and dept_id = #{userDeptId}; - 数据范围为 “指定部门”:
and dept_id in (#{deptIds})。
- 数据范围为 “本人”:
- 根据当前用户的角色,查询数据权限规则(
五、安全与可扩展考量
- 密码安全:用户密码需加盐加密(如 BCrypt),禁止明文存储;
- 接口鉴权:所有接口需校验 Token 有效性,防止未授权访问;
- 权限操作审计:记录角色分配、权限修改的日志,便于追溯;
- 缓存优化:将用户权限、角色信息缓存到 Redis,避免频繁查询数据库;
- 多租户隔离:所有表通过
tenant_id字段实现数据隔离,权限配置按租户划分; - 临时权限:可在
sys_user_permission表中增加expire_time字段,支持用户临时权限分配; - 角色互斥:在
sys_role表中增加mutual_exclude_roles字段,避免用户同时拥有互斥角色(如 “财务” 和 “审计”)。
通过以上设计,平台权限系统可实现功能级 + 数据级的精细化控制,同时具备良好的可扩展性和安全性,适配中大型平台的权限管理需求。