coder-common-thin-frontend/doc/权限管理系统文档.md
2025-07-04 15:08:19 +08:00

8.7 KiB
Raw Blame History

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)

🎭 角色系统

角色类型定义

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

核心逻辑

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. 添加新角色

  1. 定义角色类型如果使用TypeScript:
type Entity.RoleType = 'super' | 'admin' | 'editor' | 'user' | 'newRole'
  1. 在用户信息中配置角色:
const userInfo = {
  id: 1,
  userName: 'testUser',
  role: ['newRole'],  // 用户角色数组
  // ... 其他信息
}
  1. 在路由中使用新角色:
{
  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() // 或使用更优雅的方式

📚 相关文档