权限系统是企业级应用的基础模块,决定了用户能看什么、能做什么。本文将详细介绍 IMS 系统中基于 RBAC 模型的权限系统设计,包括菜单权限、数据权限与按钮级别权限。
RBAC 权限模型
RBAC(Role-Based Access Control)基于角色的访问控制是业界最通用的权限模型:
- 用户(User) - 系统的实际使用者
- 角色(Role) - 一组权限的集合,代表某个岗位或职能
- 权限(Permission) - 对系统资源的访问许可
- 资源(Resource) - 菜单、按钮、API、数据等
数据库设计
权限系统的核心表结构:
permission-tables.sql
-- 用户表 CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(128) NOT NULL, real_name VARCHAR(50), dept_id BIGINT, status TINYINT DEFAULT 1, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 角色表 CREATE TABLE sys_role ( id BIGINT PRIMARY KEY AUTO_INCREMENT, role_code VARCHAR(50) NOT NULL UNIQUE, role_name VARCHAR(100) NOT NULL, description VARCHAR(255), status TINYINT DEFAULT 1 ); -- 用户-角色关联表 CREATE TABLE sys_user_role ( user_id BIGINT, role_id BIGINT, PRIMARY KEY (user_id, role_id) ); -- 权限表 CREATE TABLE sys_permission ( id BIGINT PRIMARY KEY AUTO_INCREMENT, perm_code VARCHAR(100) NOT NULL UNIQUE, perm_name VARCHAR(100) NOT NULL, perm_type TINYINT, -- 1:菜单 2:按钮 3:API parent_id BIGINT DEFAULT 0, path VARCHAR(255), component VARCHAR(255), icon VARCHAR(50), sort_order INT DEFAULT 0 ); -- 角色-权限关联表 CREATE TABLE sys_role_permission ( role_id BIGINT, perm_id BIGINT, PRIMARY KEY (role_id, perm_id) );
权限服务实现
权限核心服务类:
permission-service.js
class PermissionService { constructor(repository) { this.repository = repository; } // 获取用户所有权限 async getUserPermissions(userId) { // 1. 获取用户所有角色 const roles = await this.repository.getUserRoles(userId); // 2. 获取角色对应的权限 const roleIds = roles.map(r => r.id); const permissions = await this.repository.getPermissionsByRoles(roleIds); // 3. 构建权限树 return this.buildPermissionTree(permissions); } // 获取用户菜单 async getUserMenus(userId) { const permissions = await this.getUserPermissions(userId); // 只保留菜单类型权限 return permissions.filter(p => p.permType === 1); } // 权限检查 async checkPermission(userId, permCode) { const userPerms = await this.repository.getUserPermCodes(userId); return userPerms.includes(permCode); } // 构建权限树 buildPermissionTree(permissions) { const map = new Map(); const roots = []; // 先将所有权限转为节点 permissions.forEach(p => { map.set(p.id, { ...p, children: [] }); }); // 构建树形结构 map.forEach(node => { if (node.parentId === 0 || !map.has(node.parentId)) { roots.push(node); } else { map.get(node.parentId).children.push(node); } }); return roots; } }
认证与授权中间件
API 级别的权限控制:
auth-middleware.js
// 权限认证中间件 const permissionMiddleware = async (ctx, next) => { // 1. 检查是否登录 const userId = ctx.session.userId; if (!userId) { return ctx.throw(401, '请先登录'); } // 2. 检查是否为超级管理员 const isAdmin = await redis.sismember(`admin:users`, userId); if (isAdmin) { return next(); } // 3. 检查接口权限 const permCode = getPermCodeFromRouter(ctx.path, ctx.method); if (permCode) { const hasPerm = await permissionService.checkPermission(userId, permCode); if (!hasPerm) { return ctx.throw(403, '无此接口权限'); } } await next(); }; // 按钮级别权限控制 const buttonPermission = async (userId, buttonCode) => { return await permissionService.checkPermission(userId, buttonCode); };
前端权限控制
前端通过权限指令控制页面元素显示:
vue-permission.js
// 权限指令 Vue.directive('permission', { inserted: (el, binding) => { const permCode = binding.value; const userPerms = store.state.user.permissions; // 没有权限则移除元素 if (!userPerms.includes(permCode)) { el.parentNode && el.parentNode.removeChild(el); } } }); // 使用示例 /*新增用户 删除 */ // 路由守卫中动态加载菜单 router.beforeEach(async (to, from, next) => { // 获取用户菜单 const menus = await api.getUserMenus(); // 动态添加路由 const accessedRoutes = filterAsyncRoutes(menus, to.matched); accessedRoutes.forEach(route => router.addRoute(route)); next({ ...to, replace: true }); });
数据权限控制
在查询时加入数据权限过滤:
data-permission.js
class DataPermissionFilter { // 获取用户数据权限范围 async getDataScope(userId) { const user = await userRepository.findById(userId); const roles = await roleRepository.findByUserId(userId); for (const role of roles) { if (role.dataScope === 1) { return { type: 'all' }; // 全部数据 } if (role.dataScope === 2) { return { type: 'dept', deptId: user.deptId }; // 本部门 } if (role.dataScope === 3) { return { type: 'deptBelow', deptId: user.deptId }; // 本部门及下属 } if (role.dataScope === 4) { return { type: 'self', userId }; // 仅自己 } } return { type: 'self', userId }; // 默认仅自己 } // 应用数据权限到查询 async applyDataScope(queryBuilder, userId) { const scope = await this.getDataScope(userId); switch (scope.type) { case 'all': break; // 不加限制 case 'dept': queryBuilder.where('dept_id', scope.deptId); break; case 'deptBelow': const deptIds = await getDeptAndChildrenIds(scope.deptId); queryBuilder.whereIn('dept_id', deptIds); break; case 'self': queryBuilder.where('create_by', scope.userId); break; } return queryBuilder; } }
权限缓存策略
为提高性能,需要对权限进行缓存:
- 用户权限缓存 - 用户登录时加载,存入 Redis,设置过期时间
- 菜单缓存 - 菜单变更时主动清除缓存
- 角色缓存 - 角色变更时清除相关用户缓存
总结
一个完善的权限系统需要考虑菜单权限、按钮权限、数据权限三个层次。通过 RBAC 模型管理角色和权限,配合前端指令和后端中间件实现多层控制。同时需要注意权限变更的实时性,使用缓存平衡性能。