Skip to content

什么是 RBAC?用户-角色-权限的表结构怎么设计? ​

  • 标签:Spring Security、认证授权、后端

🏛 业务架构师 ​

RBAC(Role-Based Access Control,基于角色的访问控制):用户 → 角色 → 权限 的间接授权模型——权限挂在角色上,用户通过角色获得权限。价值:授权管理从"每用户配每权限"(M×N 爆炸)变成"角色"这一中间层批量管理——入职给角色、转岗换角色、离职删关联,权限治理有了组织化的抓手。

经典表结构(五表模型):

sql
-- 用户表
user(id, username, password_hash, status, ...)
-- 角色表
role(id, role_code, role_name, ...)          -- 如 ADMIN/OPERATOR
-- 权限表(菜单+按钮/API 两类)
permission(id, perm_code, perm_name, resource, action)
-- 用户-角色关联(多对多)
user_role(user_id, role_id)
-- 角色-权限关联(多对多)
role_permission(role_id, permission_id)

设计要点(踩坑经验):

  1. 多租户:企业 SaaS 必须给五表都加 tenant_id,且角色体系按租户隔离(每租户自定义角色)或"平台预置+租户扩展"分层;
  2. 权限粒度三层:菜单/页面(前端显隐)、API/操作(perm_code 对应 Spring Security 的 authority,如 order:delete)、数据行级(区域/部门过滤——RBAC 管不了数据权限,要叠加数据规则表或部门树 scope 字段);
  3. 角色爆炸防治:控制角色数量(超 30 个说明粒度失控);需要"临时组合"时用"角色+用户直接授权"混合(RBAC1 的继承慎用,复杂度不可控);
  4. 审计字段:谁授的权、何时授的、何时过期——等保合规必备。

与 Spring Security 对接:登录时把用户的所有 permission perm_code 聚合为 authorities 装进 Authentication,@PreAuthorize("hasAuthority('order:delete')") 直接生效——表结构设计时 perm_code 命名规范(资源:动作)就是未来的注解文案。

🏗 系统架构师 ​

RBAC(Role-Based Access Control)= 用户 → 角色 → 权限的间接授权模型:权限挂角色、用户挂角色——间接层带来管理复杂度从 M×N 降为 M+N(千人系统直接管"人×权限"是不可能的)。

标准五表设计(生产版含实体表):

sql
-- 用户表
user(id, username, password_hash, status, ...)
-- 角色表
role(id, code, name, data_scope)          -- data_scope: 数据权限范围标记
-- 权限表(两级:菜单/API 归一建模)
permission(id, code, name, type,          -- MENU / BUTTON / API
           parent_id, path, sort)
-- 用户-角色
user_role(user_id, role_id)               -- UQ(user_id, role_id)
-- 角色-权限
role_permission(role_id, permission_id)

-- 数据级权限(进阶):规则挂在角色上
-- role_data_rule(role_id, table_name, rule_expr)

设计要点(架构师经验):

  • 权限点建模统一:菜单、按钮、API 端点都进 permission(type 区分)——前后端同一套权限码(order:export),前端控显隐、后端控执行,双重校验(前端控显只是体验,后端才是防线);
  • 角色是稳定职责的抽象:按岗位/职责定义(运营专员、财务审核),拒绝"一人一角色"退化(角色爆炸 = RBAC 失效信号);临时授权走"角色 + 有效期"扩展列;
  • 用户多角色权限并集,冲突时显式 deny 优先或按最严格策略(团队要统一约定);
  • 数据权限分离:功能权限(能不能调)与数据权限(能看哪些行:本人/本部门/全部)是两个维度——data_scope 字段或独立规则表,SQL 层注入过滤条件(MyBatis 拦截器/SpEL),不要在业务代码里散落 if;
  • 规模化演进:RBAC → 加属性(ABAC:部门、密级、区域动态判定)→ 策略引擎外置(权限即配置)。

缓存与一致性:权限集缓存(Redis + 用户级版本号),改权限广播失效——权限变更的生效延迟要明确 SLA(踢人场景见 JWT 注销专题)。

💻 全栈开发专家 ​

RBAC(Role-Based Access Control,基于角色的访问控制):用户 → 角色 → 权限 三级映射——权限挂在角色上,用户通过角色获得权限。价值:授权管理从「每人每资源」变成「角色定义一次、成员批量管」。

标准表结构(五表模型,我的生产版):

sql
-- 用户表
CREATE TABLE sys_user (
    id BIGINT PRIMARY KEY,
    username VARCHAR(64) NOT NULL UNIQUE,
    password VARCHAR(100) NOT NULL,          -- BCrypt
    enabled TINYINT DEFAULT 1
);

-- 角色表(权限的聚合单位)
CREATE TABLE sys_role (
    id BIGINT PRIMARY KEY,
    role_code VARCHAR(64) NOT NULL UNIQUE,   -- ADMIN / ORDER_MGR,代码里用 code
    role_name VARCHAR(64)
);

-- 权限表(原子操作点)
CREATE TABLE sys_permission (
    id BIGINT PRIMARY KEY,
    perm_code VARCHAR(128) NOT NULL UNIQUE,  -- order:read / order:refund
    resource VARCHAR(64), method VARCHAR(16) -- 可选:绑定菜单/按钮/接口
);

CREATE TABLE sys_user_role (            -- 用户-角色 N:N
    user_id BIGINT, role_id BIGINT,
    PRIMARY KEY(user_id, role_id)
);
CREATE TABLE sys_role_permission (      -- 角色-权限 N:N
    role_id BIGINT, perm_id BIGINT,
    PRIMARY KEY(role_id, perm_id)
);

设计要点(实战):

  1. code 参与鉴权,name 给人看——权限判断 hasAuthority("order:refund"),改名不影响代码;
  2. 权限粒度到操作点(资源:动作),不到 URL(URL 与权限解耦,前端菜单和后端接口共用一套);
  3. 用户角色变更 → 踢缓存重登录(JWT 场景配 token_version);
  4. 扩展位:角色加 data_scope 字段(本部门/本部门及以下/全部)做数据权限;多租户加 tenant_id 全表贯穿;
  5. 别过度设计:小系统角色直接嵌权限(三表)就够,五表是权限点会持续膨胀时才需要。

加载到 Spring Security:UserDetails.authorities = permissions(角色聚合后去重的权限集)。