TUTORIAL

用户权限管理系统设计

用户权限管理系统设计

用户权限管理系统设计

企业官网后台几乎都离不开权限管理——谁能看文章列表、谁能发布内容、谁能管理用户。直接在每个接口里写 if (user.role == 'admin') 看似简单,但角色一多就难以维护。业界通用的解决方案是 RBAC(基于角色的访问控制),本篇带你从模型到落地完整理解它。

一、RBAC 权限模型

RBAC 的核心思想是"用户不直接拥有权限,而是通过角色间接获得权限"。一个用户可以被赋予多个角色,一个角色可以拥有多个权限,权限对应具体的操作(如"文章-发布""用户-删除")。这样用户与权限解耦,新增角色或调整权限时不必修改用户数据。

相比"用户直接绑权限"的扁平模型,RBAC 的优势在于:第一,权限调整集中发生在角色上,批量生效;第二,角色名(如"编辑""审核员")贴合业务语义,配置直观;第三,便于审计——想知道某用户能做什么,看他的角色即可。对于企业官网这种角色相对固定(管理员、编辑、运营、访客)的场景,RBAC 是最合适的选择。

  • 用户(User):系统登录主体,如 admin、editor。
  • 角色(Role):权限的集合,如"内容编辑""超级管理员"。
  • 权限(Permission):最小操作单元,如"article:publish"。
  • 分配关系:用户-角色多对多,角色-权限多对多。

二、用户-角色-权限表设计

经典的 RBAC 需要五张表:用户表、角色表、权限表,以及两张关联表。关联表用复合主键保证不重复分配,并加时间戳记录分配时机。

-- 用户表
CREATE TABLE sys_user (
  id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
  username VARCHAR(32) NOT NULL UNIQUE,
  password VARCHAR(128) NOT NULL COMMENT '加密后密码',
  status TINYINT(1) NOT NULL DEFAULT 1 COMMENT '1启用0禁用',
  create_time DATETIME NOT NULL
);

-- 角色表
CREATE TABLE sys_role (
  id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
  role_name VARCHAR(32) NOT NULL UNIQUE,
  role_code VARCHAR(32) NOT NULL COMMENT '角色编码',
  description VARCHAR(120)
);

-- 权限表
CREATE TABLE sys_permission (
  id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
  perm_code VARCHAR(64) NOT NULL UNIQUE COMMENT '如 article:publish',
  perm_name VARCHAR(64) NOT NULL,
  module VARCHAR(32) NOT NULL COMMENT '所属模块'
);

-- 用户-角色关联
CREATE TABLE sys_user_role (
  user_id BIGINT UNSIGNED NOT NULL,
  role_id BIGINT UNSIGNED NOT NULL,
  PRIMARY KEY (user_id, role_id)
);

-- 角色-权限关联
CREATE TABLE sys_role_permission (
  role_id BIGINT UNSIGNED NOT NULL,
  perm_id BIGINT UNSIGNED NOT NULL,
  PRIMARY KEY (role_id, perm_id)
);

三、权限校验落地

表结构建好后,登录时把该用户的所有权限码查出并缓存(建议放 Redis,避免每次请求查库)。校验时只需判断目标权限码是否在用户权限集合里。下面是伪代码示例:

// 登录时加载权限并缓存
function loadUserPermissions(userId) {
  const perms = db.query(`
    SELECT p.perm_code
    FROM sys_user_role ur
    JOIN sys_role_permission rp ON ur.role_id = rp.role_id
    JOIN sys_permission p ON rp.perm_id = p.id
    WHERE ur.user_id = ?`, userId);
  cache.set(`perms:${userId}`, perms.map(p => p.perm_code));
}

// 接口入口校验
function checkPermission(userId, requiredCode) {
  const perms = cache.get(`perms:${userId}`);
  if (!perms.includes(requiredCode)) {
    throw new Error('无操作权限');
  }
}

两个细节值得注意:第一,权限码命名建议用"模块:操作"格式(如 article:delete),既可精确校验单操作,也可按模块前缀批量校验;第二,角色调整后必须刷新缓存,否则用户权限不生效。可以在分配/取消角色时主动清除对应用户的权限缓存,保证实时性。这套机制在尧图多个企业后台项目中稳定运行,足以应对绝大多数权限场景。

返回教程列表