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