390 lines
8.7 KiB
Markdown
390 lines
8.7 KiB
Markdown
# Nova Admin 权限系统文档
|
||
|
||
## 概述
|
||
|
||
Nova Admin 采用基于角色的访问控制(RBAC)模型,通过用户角色和权限指令实现细粒度的权限控制。系统支持路由级别和组件级别的权限验证。
|
||
|
||
## 🏗️ 权限架构
|
||
|
||
### 核心组件
|
||
|
||
1. **权限Hook** (`src/hooks/usePermission.ts`)
|
||
2. **权限指令** (`src/directives/permission.ts`)
|
||
3. **路由权限过滤** (`src/store/router/helper.ts`)
|
||
4. **用户角色管理** (`src/store/auth.ts`)
|
||
|
||
## 🎭 角色系统
|
||
|
||
### 角色类型定义
|
||
|
||
```typescript
|
||
type Entity.RoleType = string // 如: 'admin', 'user', 'super', 'editor'
|
||
```
|
||
|
||
### 内置角色层级
|
||
|
||
1. **super**: 超级管理员,拥有所有权限
|
||
2. **admin**: 管理员角色
|
||
3. **user**: 普通用户角色
|
||
4. **editor**: 编辑者角色
|
||
|
||
### 角色权限矩阵
|
||
|
||
| 功能模块 | super | admin | editor | user |
|
||
|---------|-------|-------|--------|------|
|
||
| 仪表盘 | ✅ | ✅ | ✅ | ✅ |
|
||
| 用户管理 | ✅ | ✅ | ❌ | ❌ |
|
||
| 系统设置 | ✅ | ✅ | ❌ | ❌ |
|
||
| 内容编辑 | ✅ | ✅ | ✅ | ❌ |
|
||
|
||
## 🔐 权限验证机制
|
||
|
||
### 1. usePermission Hook
|
||
|
||
位置:`src/hooks/usePermission.ts`
|
||
|
||
#### 核心逻辑
|
||
|
||
```typescript
|
||
function hasPermission(permission?: Entity.RoleType | Entity.RoleType[]) {
|
||
if (!permission) return true // 无权限要求,直接通过
|
||
|
||
if (!authStore.userInfo) return false // 未登录,拒绝访问
|
||
|
||
const { role } = authStore.userInfo
|
||
|
||
// super 角色拥有所有权限
|
||
let has = role.includes('super')
|
||
|
||
if (!has) {
|
||
if (isArray(permission)) {
|
||
// 权限为数组,判断是否有交集
|
||
has = permission.some(i => role.includes(i))
|
||
}
|
||
if (isString(permission)) {
|
||
// 权限为字符串,判断是否包含
|
||
has = role.includes(permission)
|
||
}
|
||
}
|
||
|
||
return has
|
||
}
|
||
```
|
||
|
||
#### 使用方式
|
||
|
||
```typescript
|
||
import { usePermission } from '@/hooks'
|
||
|
||
const { hasPermission } = usePermission()
|
||
|
||
// 检查单个角色
|
||
if (hasPermission('admin')) {
|
||
// 用户拥有 admin 角色
|
||
}
|
||
|
||
// 检查多个角色(任一匹配)
|
||
if (hasPermission(['admin', 'editor'])) {
|
||
// 用户拥有 admin 或 editor 角色
|
||
}
|
||
```
|
||
|
||
### 2. 权限指令
|
||
|
||
位置:`src/directives/permission.ts`
|
||
|
||
#### 指令实现
|
||
|
||
```typescript
|
||
const permissionDirective: Directive<HTMLElement, Entity.RoleType | Entity.RoleType[]> = {
|
||
mounted(el, binding) {
|
||
updatapermission(el, binding.value)
|
||
},
|
||
updated(el, binding) {
|
||
updatapermission(el, binding.value)
|
||
},
|
||
}
|
||
|
||
function updatapermission(el: HTMLElement, permission: Entity.RoleType | Entity.RoleType[]) {
|
||
if (!permission) {
|
||
throw new Error('v-permission Directive with no explicit role attached')
|
||
}
|
||
|
||
if (!hasPermission(permission)) {
|
||
el.parentElement?.removeChild(el) // 移除DOM元素
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 使用方式
|
||
|
||
```vue
|
||
<template>
|
||
<!-- 只有 admin 角色可见 -->
|
||
<button v-permission="'admin'">管理员功能</button>
|
||
|
||
<!-- admin 或 editor 角色可见 -->
|
||
<div v-permission="['admin', 'editor']">
|
||
编辑内容
|
||
</div>
|
||
|
||
<!-- super 角色可见 -->
|
||
<section v-permission="'super'">
|
||
超级管理员专用区域
|
||
</section>
|
||
</template>
|
||
```
|
||
|
||
## 🛣️ 路由权限控制
|
||
|
||
### 路由元信息配置
|
||
|
||
```typescript
|
||
interface AppRoute.RowRoute {
|
||
// ... 其他属性
|
||
roles?: Entity.RoleType[] // 访问路由所需的角色
|
||
requiresAuth?: boolean // 是否需要登录
|
||
}
|
||
```
|
||
|
||
### 权限路由示例
|
||
|
||
```typescript
|
||
export const staticRoutes: AppRoute.RowRoute[] = [
|
||
{
|
||
name: 'userManagement',
|
||
path: '/setting/account',
|
||
title: '用户管理',
|
||
requiresAuth: true,
|
||
roles: ['super', 'admin'], // 只有 super 或 admin 可访问
|
||
componentPath: '/setting/account/index.vue',
|
||
},
|
||
{
|
||
name: 'superOnly',
|
||
path: '/admin/super',
|
||
title: '超级管理',
|
||
requiresAuth: true,
|
||
roles: ['super'], // 只有 super 可访问
|
||
componentPath: '/admin/super/index.vue',
|
||
}
|
||
]
|
||
```
|
||
|
||
### 路由权限过滤
|
||
|
||
在 `src/store/router/helper.ts` 中实现:
|
||
|
||
```typescript
|
||
export function createRoutes(routes: AppRoute.RowRoute[]) {
|
||
const { hasPermission } = usePermission()
|
||
|
||
// 权限过滤
|
||
let resultRouter = standardizedRoutes(routes)
|
||
resultRouter = resultRouter.filter(i => hasPermission(i.meta.roles))
|
||
|
||
// ... 其他处理
|
||
return resultRouter
|
||
}
|
||
```
|
||
|
||
## 📋 权限验证流程
|
||
|
||
### 1. 路由级权限验证
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[用户访问路由] --> B{路由需要权限?}
|
||
B -->|否| G[允许访问]
|
||
B -->|是| C{用户已登录?}
|
||
C -->|否| D[重定向到登录页]
|
||
C -->|是| E{用户角色匹配?}
|
||
E -->|否| F[显示403错误]
|
||
E -->|是| G[允许访问]
|
||
```
|
||
|
||
### 2. 组件级权限验证
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[组件渲染] --> B{使用了权限指令?}
|
||
B -->|否| F[正常渲染]
|
||
B -->|是| C{用户角色匹配?}
|
||
C -->|否| D[移除DOM元素]
|
||
C -->|是| E[正常显示元素]
|
||
```
|
||
|
||
### 3. API级权限验证
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[调用API] --> B[请求携带Token]
|
||
B --> C[后端验证Token]
|
||
C --> D{Token有效?}
|
||
D -->|否| E[返回401错误]
|
||
D -->|是| F{用户权限足够?}
|
||
F -->|否| G[返回403错误]
|
||
F -->|是| H[返回数据]
|
||
```
|
||
|
||
## 🛠️ 开发指南
|
||
|
||
### 1. 添加新角色
|
||
|
||
1. **定义角色类型**(如果使用TypeScript):
|
||
```typescript
|
||
type Entity.RoleType = 'super' | 'admin' | 'editor' | 'user' | 'newRole'
|
||
```
|
||
|
||
2. **在用户信息中配置角色**:
|
||
```typescript
|
||
const userInfo = {
|
||
id: 1,
|
||
userName: 'testUser',
|
||
role: ['newRole'], // 用户角色数组
|
||
// ... 其他信息
|
||
}
|
||
```
|
||
|
||
3. **在路由中使用新角色**:
|
||
```typescript
|
||
{
|
||
name: 'newFeature',
|
||
path: '/new-feature',
|
||
roles: ['newRole'], // 新角色权限
|
||
// ... 其他配置
|
||
}
|
||
```
|
||
|
||
### 2. 组件中权限检查
|
||
|
||
```vue
|
||
<script setup>
|
||
import { usePermission } from '@/hooks'
|
||
|
||
const { hasPermission } = usePermission()
|
||
|
||
// 编程式权限检查
|
||
const canEdit = computed(() => hasPermission(['admin', 'editor']))
|
||
const canDelete = computed(() => hasPermission('admin'))
|
||
</script>
|
||
|
||
<template>
|
||
<div>
|
||
<!-- 指令式权限控制 -->
|
||
<button v-permission="'admin'">删除</button>
|
||
|
||
<!-- 编程式权限控制 -->
|
||
<button v-if="canEdit">编辑</button>
|
||
|
||
<!-- 条件渲染 -->
|
||
<div v-if="hasPermission('super')">
|
||
超级管理员专用功能
|
||
</div>
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
### 3. 动态权限控制
|
||
|
||
```typescript
|
||
// 动态检查权限
|
||
function checkDynamicPermission(action: string) {
|
||
const requiredRoles = getRequiredRolesForAction(action)
|
||
return hasPermission(requiredRoles)
|
||
}
|
||
|
||
// 根据权限显示菜单
|
||
const visibleMenuItems = computed(() => {
|
||
return menuItems.filter(item => hasPermission(item.requiredRoles))
|
||
})
|
||
```
|
||
|
||
## 🔧 配置选项
|
||
|
||
### 权限配置文件
|
||
|
||
可以创建权限配置文件来集中管理权限:
|
||
|
||
```typescript
|
||
// src/config/permissions.ts
|
||
export const PERMISSIONS = {
|
||
USER_MANAGEMENT: ['super', 'admin'],
|
||
CONTENT_EDIT: ['super', 'admin', 'editor'],
|
||
VIEW_DASHBOARD: ['super', 'admin', 'editor', 'user'],
|
||
SYSTEM_CONFIG: ['super'],
|
||
} as const
|
||
|
||
// 使用配置
|
||
hasPermission(PERMISSIONS.USER_MANAGEMENT)
|
||
```
|
||
|
||
### 默认权限行为
|
||
|
||
```typescript
|
||
// 默认情况下的权限行为
|
||
const defaultPermissionBehavior = {
|
||
// 未指定权限时的默认行为
|
||
noPermissionRequired: true,
|
||
|
||
// 超级管理员绕过所有权限检查
|
||
superAdminBypass: true,
|
||
|
||
// 权限检查失败时的行为
|
||
onPermissionDenied: 'hide', // 'hide' | 'disable' | 'redirect'
|
||
}
|
||
```
|
||
|
||
## 🚨 最佳实践
|
||
|
||
### 1. 权限粒度
|
||
|
||
- **页面级权限**: 控制整个页面的访问
|
||
- **功能级权限**: 控制页面内特定功能
|
||
- **数据级权限**: 控制数据的增删改查
|
||
|
||
### 2. 权限设计原则
|
||
|
||
- **最小权限原则**: 默认拒绝,明确授权
|
||
- **角色继承**: 高级角色包含低级角色的权限
|
||
- **权限组合**: 支持多角色的权限组合
|
||
|
||
### 3. 安全考虑
|
||
|
||
- **前端权限仅用于UI控制**: 不能作为安全防护的唯一手段
|
||
- **后端验证**: 所有敏感操作必须在后端验证权限
|
||
- **权限缓存**: 合理缓存权限信息,避免频繁查询
|
||
|
||
## 🐛 常见问题
|
||
|
||
### 1. 权限指令不生效
|
||
|
||
检查以下几点:
|
||
- 指令是否正确注册
|
||
- 权限值是否正确传递
|
||
- 用户角色信息是否正确加载
|
||
|
||
### 2. 动态路由权限问题
|
||
|
||
确保在路由初始化时正确过滤权限:
|
||
```typescript
|
||
// 在 routeStore.initAuthRoute() 中
|
||
const routes = createRoutes(rowRoutes) // 这里会进行权限过滤
|
||
```
|
||
|
||
### 3. 权限更新不及时
|
||
|
||
当用户权限发生变化时:
|
||
```typescript
|
||
// 重新初始化路由
|
||
await routeStore.initAuthRoute()
|
||
|
||
// 刷新页面权限
|
||
window.location.reload() // 或使用更优雅的方式
|
||
```
|
||
|
||
## 📚 相关文档
|
||
|
||
- [登录鉴权文档](./登录鉴权系统文档)
|
||
- [路由系统文档](./路由管理系统文档)
|
||
- [整体架构文档](./整体架构文档)
|