本文目录导读:
全栈项目的权限分级设计是一个核心问题,设计得好能让系统安全、易维护,设计得差则可能导致安全漏洞或代码混乱。
下面从基础模型、后端实现、前端控制、数据库设计和最佳实践五个层面来拆解。
核心基础模型:RBAC(基于角色的访问控制)
绝大多数全栈项目都采用 RBAC 模型,它的核心思想是:用户 -> 角色 -> 权限。
- 用户 (User):系统的使用者。
- 角色 (Role):权限的集合,如“管理员”、“编辑者”、“普通用户”。用户不直接拥有权限,而是拥有角色。
- 权限 (Permission):对某个资源的操作能力,如“创建文章”、“删除用户”、“查看报表”。
为什么用 RBAC? 它解决了“给每个用户单独分配权限”的痛点,你只需要管理少数几个角色,然后给用户分配角色即可,极大降低了维护成本。
后端设计(以 Node.js + Express 为例)
后端是权限控制的基石,必须做到“后端不可信”,前端可以隐藏按钮,但后端必须校验。
数据库表结构设计(核心)
通常需要三张核心表,两张中间表:
users(用户表):id,username,password,role_id(外键,关联到角色)roles(角色表):id,name(如admin,editor,user),descriptionpermissions(权限表):id,name(如article:create),descriptionrole_permissions(角色-权限关联表):role_id,permission_iduser_roles(用户-角色关联表,可选):如果你的用户只有一个角色,可以直接在users表里用role_id,如果用户可以有多个角色,则需要这张关联表。
权限命名规范(重要):
推荐使用 资源:操作 或 资源.操作 的格式,便于统一管理。
system:user:list(查看用户列表)system:user:create(创建用户)system:user:update(更新用户)system:user:delete(删除用户)article:list(查看文章列表)article:create(创建文章)article:edit(编辑自己的文章)article:publish(发布文章 - 通常只有管理员有)
后端中间件实现
你需要实现一个 权限校验中间件。
// 伪代码示例
// middleware/auth.js
// 1. 验证用户是否登录 (JWT鉴权)
function authenticate(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
try {
const decoded = jwt.verify(token, SECRET_KEY);
req.user = decoded; // { userId: 1, role: 'admin' }
next();
} catch (err) {
return res.status(401).json({ error: 'Unauthorized' });
}
}
// 2. 权限校验中间件 (核心)
function authorize(...requiredPermissions) {
return (req, res, next) => {
// 从用户角色中获取所有权限 (建议在登录时缓存到JWT或Redis中)
const userPermissions = getUserPermissionsFromRole(req.user.role);
// 或者:const userPermissions = getUserPermissionsFromDatabase(req.user.userId);
// 检查是否拥有所有必需的权限
const hasPermission = requiredPermissions.every(permission =>
userPermissions.includes(permission)
);
if (hasPermission) {
next();
} else {
return res.status(403).json({ error: 'Forbidden' });
}
};
}
// 3. 在路由中使用
router.get('/api/users',
authenticate,
authorize('system:user:list'),
userController.listUsers
);
router.delete('/api/users/:id',
authenticate,
authorize('system:user:delete'),
userController.deleteUser
);
前端实现
前端的权限控制主要是 用户体验 问题,不能依赖它做安全防护。
获取用户权限
用户登录成功后,后端在返回的JWT中(或另外调用 /api/user/permissions 接口)包含用户的角色和权限列表。
// 后端返回数据结构
{
"user": {
"id": 1,
"username": "admin",
"role": "admin"
},
"permissions": [
"system:user:list",
"system:user:create",
"system:user:delete",
"article:create",
"article:publish"
]
}
// 前端可以将其存入全局状态管理 (如 Pinia/Vuex, Redux/Zustand)
组件级别的控制
-
按钮、菜单的显隐:
// React 示例 import { usePermissions } from '../hooks/usePermissions'; function UserListPage() { const { hasPermission } = usePermissions(); return ( <div> <h1>用户管理</h1> {hasPermission('system:user:create') && ( <button onClick={openCreateModal}>新建用户</button> )} {/* 删除按钮同理 */} </div> ); } -
路由级别的控制:
// 实现一个 PrivateRoute 组件或路由守卫 function PrivateRoute({ children, requiredPermissions }) { const { hasPermission } = usePermissions(); const hasAll = requiredPermissions.every(p => hasPermission(p)); if (!hasAll) { return <Navigate to="/403" replace />; // 或者 <Redirect /> } return children; } // 路由配置 <Route path="/admin/users" element={ <PrivateRoute requiredPermissions={['system:user:list']}> <UserListPage /> </PrivateRoute> } />
更精细化的设计扩展
除了基础的 RBAC,实际项目中可能还需要以下扩展:
数据权限(行级权限)
-
问题:同样是“查看文章”,普通用户只能看自己的文章,管理员能看到所有人的。
-
解决:在数据库查询时,根据用户的角色和ID拼接不同的
WHERE条件。-- 管理员:查全部 SELECT * FROM articles; -- 普通用户:查自己的 SELECT * FROM articles WHERE user_id = :currentUserId;
-
常见模型:按
用户 -> 部门 -> 集团等组织架构划分数据范围。
字段权限(列级权限)
- 问题:普通用户查看用户信息时,不能看到“手机号”、“身份证号”。
- 解决:后端返回数据时,根据权限过滤掉敏感字段。
// controller 中 const userData = await User.findById(id); if (!req.user.permissions.includes('user:view:phone')) { delete userData.phone; // 脱敏 }
多租户权限(SaaS 场景)
- 问题:不同公司的用户登录后,只能看到自己公司(租户)内的数据。
- 解决:在所有涉及到数据的表中都增加一个
tenant_id字段,数据库查询时必须带上WHERE tenant_id = :currentTenantId。
关键最佳实践
- 永远不要相信前端:前端只是装饰,后端必须对每一次请求进行独立的权限校验。
- 最小权限原则:给每个角色分配刚刚好的权限,默认不开放任何权限,只开放你需要的。
- 权限缓存:不要每次请求都查一次数据库,可以将用户的权限列表缓存在:
- JWT Token 中(适合权限变动不频繁的场景)。
- Redis 中(适合权限经常变动的场景,设置过期时间)。
- 权限固化:代码中直接使用权限字符串(如
system:user:delete),而不是通过动态配置,权限的增删改应该由代码发布来控制,而不是数据库随意修改。 - 避免超管角色:不要设计一个可以绕过所有权限检查的超级管理员角色,如果有超级管理员,它的处理方式应该是:在数据库或配置中预置它的权限列表,并且不要公开它的密码。
一个成熟的全栈项目权限分级设计,可以概括为:
一个模型(RBAC),两层控制(后端强校验 + 前端弱展示),三个维度(功能权限、数据权限、字段权限)。
如果你开发的团队只有几个人、业务逻辑不复杂,可以直接用 用户 -> 角色 -> 权限 的经典 RBAC,随着系统复杂,再逐步引入数据权限和多租户。
标签: 权限粒度