library-picturebook-activity/docs/MENU_PERMISSION_CONTROL.md
2025-11-23 14:04:20 +08:00

247 lines
6.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 菜单权限控制说明
## 📋 概述
系统通过 **权限编码Permission Code** 来控制用户对菜单的访问。菜单权限控制分为两个层面:
1. **菜单显示控制**:根据用户权限过滤菜单,只显示用户有权限访问的菜单
2. **路由访问控制**:通过路由守卫检查用户是否有权限访问某个页面
## 🔄 权限控制流程
```
用户登录
获取用户角色和权限
调用 /api/menus/user-menus 获取用户菜单
后端根据用户权限过滤菜单
前端动态生成路由和菜单
路由守卫检查页面访问权限
```
## 🎯 如何配置菜单权限
### 1. 创建权限
首先需要在权限管理中创建权限,例如:
- `menu:read` - 查看菜单权限
- `user:read` - 查看用户权限
- `role:read` - 查看角色权限
### 2. 将权限分配给角色
在角色管理中,为角色分配相应的权限。例如:
- **管理员角色**:拥有所有权限
- **普通用户角色**:只拥有 `user:read` 权限
### 3. 为用户分配角色
在用户管理中,为用户分配角色。用户会继承角色的所有权限。
### 4. 为菜单设置权限编码
在菜单管理中,为菜单设置 `权限编码` 字段:
#### 示例配置
| 菜单名称 | 路径 | 权限编码 | 说明 |
| -------- | ------------------- | ----------- | --------------------------------------------- |
| 用户管理 | /system/users | `user:read` | 只有拥有 `user:read` 权限的用户才能看到此菜单 |
| 角色管理 | /system/roles | `role:read` | 只有拥有 `role:read` 权限的用户才能看到此菜单 |
| 权限管理 | /system/permissions | - | 不设置权限编码,所有用户都可以看到 |
| 仪表盘 | /dashboard | - | 不设置权限编码,所有用户都可以看到 |
### 5. 权限编码规则
权限编码格式:`资源:操作`
常见示例:
- `user:read` - 查看用户
- `user:create` - 创建用户
- `user:update` - 更新用户
- `user:delete` - 删除用户
- `role:read` - 查看角色
- `menu:read` - 查看菜单
## 💻 技术实现
### 后端实现
#### 1. 菜单权限过滤(`MenusService.findUserMenus`
```typescript
// 获取用户的所有权限
const userPermissions = await this.authService.getUserPermissions(userId);
// 过滤菜单如果菜单有permission字段检查用户是否有该权限
const filterMenus = (menus: any[]): any[] => {
return menus
.filter((menu) => {
// 如果菜单没有设置权限要求,则显示
if (!menu.permission) {
return true;
}
// 如果设置了权限要求,检查用户是否有该权限
return userPermissions.includes(menu.permission);
})
.map((menu) => {
// 递归过滤子菜单
if (menu.children && menu.children.length > 0) {
menu.children = filterMenus(menu.children);
}
return menu;
});
};
```
#### 2. 用户权限获取(`AuthService.getUserPermissions`
```typescript
async getUserPermissions(userId: number): Promise<string[]> {
const user = await this.usersService.findOne(userId);
const permissions = new Set<string>();
// 遍历用户的所有角色
user.roles?.forEach((ur: any) => {
// 遍历角色的所有权限
ur.role.permissions?.forEach((rp: any) => {
permissions.add(rp.permission.code);
});
});
return Array.from(permissions);
}
```
### 前端实现
#### 1. 菜单转换为路由(`convertMenusToRoutes`
```typescript
const route: RouteRecordRaw = {
path: routePath,
name: routeName,
meta: {
title: menu.name,
requiresAuth: true,
// 如果菜单有权限要求添加到路由meta中
...(menu.permission && { permissions: [menu.permission] }),
},
component: componentLoader,
};
```
#### 2. 路由守卫检查(`router.beforeEach`
```typescript
// 检查权限
const requiredPermissions = to.meta.permissions;
if (requiredPermissions && requiredPermissions.length > 0) {
if (!authStore.hasAnyPermission(requiredPermissions)) {
// 没有所需权限,跳转到 403 页面
next({ name: "Forbidden" });
return;
}
}
```
#### 3. 权限检查方法(`authStore`
```typescript
// 检查是否有指定权限
const hasPermission = (permission: string): boolean => {
return user.value?.permissions?.includes(permission) ?? false;
};
// 检查是否有任一权限
const hasAnyPermission = (permissions: string[]): boolean => {
if (!permissions || permissions.length === 0) return true;
return permissions.some((perm) => hasPermission(perm));
};
```
## 📝 使用示例
### 示例 1创建需要权限的菜单
1. 登录系统,进入 **菜单管理**
2. 点击 **新增菜单**
3. 填写菜单信息:
- 菜单名称:`用户管理`
- 路由路径:`/system/users`
- 组件路径:`system/users/Index`
- **权限编码**`user:read` ⭐
- 父菜单:选择 `系统管理`
4. 保存菜单
### 示例 2创建公开菜单所有用户可见
1. 在菜单管理中新增菜单
2. **权限编码字段留空**
3. 这样所有用户都可以看到此菜单
### 示例 3为用户分配权限
1. 进入 **权限管理**,创建权限:
- 权限名称:`查看用户`
- 权限编码:`user:read`
- 资源:`user`
- 操作:`read`
2. 进入 **角色管理**,编辑角色:
- 为角色分配 `user:read` 权限
3. 进入 **用户管理**,编辑用户:
- 为用户分配该角色
## ⚠️ 注意事项
1. **权限编码必须唯一**:每个权限编码在系统中是唯一的
2. **菜单权限为空则公开**:如果菜单的 `权限编码` 字段为空,所有用户都可以看到
3. **子菜单继承父菜单权限**:子菜单会独立检查权限,不会自动继承父菜单权限
4. **路由和菜单双重控制**
- 菜单显示控制:控制菜单是否在侧边栏显示
- 路由访问控制:控制用户是否可以直接访问页面(通过 URL
5. **权限变更后需重新登录**:权限变更后,用户需要重新登录才能看到新的菜单
## 🔍 调试技巧
### 1. 查看用户权限
在浏览器控制台执行:
```javascript
// 查看当前用户权限
console.log(useAuthStore().user?.permissions);
// 检查是否有特定权限
console.log(useAuthStore().hasPermission("user:read"));
```
### 2. 查看用户菜单
```javascript
// 查看当前用户的菜单
console.log(useAuthStore().menus);
```
### 3. 后端调试
在后端日志中查看:
- 用户权限列表
- 菜单过滤结果
## 📚 相关文档
- [RBAC 权限控制详解](./RBAC_GUIDE.md)
- [菜单管理使用说明](./MENU_MANAGEMENT.md)
- [权限管理使用说明](./PERMISSION_MANAGEMENT.md)