Skip to content

Latest commit

 

History

History
610 lines (513 loc) · 14.3 KB

File metadata and controls

610 lines (513 loc) · 14.3 KB

isAuth.js 权限验证工具文档

概述

isAuth.js是权限验证工具模块,提供用户权限检查、角色验证、菜单权限等功能。

文件位置: src/common/isAuth.js

核心功能

1. 权限检查函数

/**
 * 检查用户是否有指定权限
 * @param {string|Array} permission - 权限标识或权限数组
 * @param {string} operator - 操作符 'AND'|'OR',默认'OR'
 * @returns {boolean} 是否有权限
 */
export const hasPermission = (permission, operator = 'OR') => {
  const userStore = useUserStore()
  const userPermissions = userStore.permissions || []
  
  if (!permission) return true
  
  // 超级管理员拥有所有权限
  if (userStore.isSuperAdmin) return true
  
  // 单个权限检查
  if (typeof permission === 'string') {
    return userPermissions.includes(permission)
  }
  
  // 多个权限检查
  if (Array.isArray(permission)) {
    if (operator === 'AND') {
      // 需要拥有所有权限
      return permission.every(p => userPermissions.includes(p))
    } else {
      // 拥有任一权限即可
      return permission.some(p => userPermissions.includes(p))
    }
  }
  
  return false
}

2. 角色检查函数

/**
 * 检查用户是否有指定角色
 * @param {string|Array} role - 角色标识或角色数组
 * @param {string} operator - 操作符 'AND'|'OR',默认'OR'
 * @returns {boolean} 是否有角色
 */
export const hasRole = (role, operator = 'OR') => {
  const userStore = useUserStore()
  const userRoles = userStore.roles || []
  
  if (!role) return true
  
  // 超级管理员拥有所有角色
  if (userStore.isSuperAdmin) return true
  
  // 单个角色检查
  if (typeof role === 'string') {
    return userRoles.some(r => r.code === role)
  }
  
  // 多个角色检查
  if (Array.isArray(role)) {
    if (operator === 'AND') {
      // 需要拥有所有角色
      return role.every(r => userRoles.some(ur => ur.code === r))
    } else {
      // 拥有任一角色即可
      return role.some(r => userRoles.some(ur => ur.code === r))
    }
  }
  
  return false
}

3. 菜单权限检查

/**
 * 检查用户是否可以访问指定菜单
 * @param {string} menuPath - 菜单路径
 * @returns {boolean} 是否可以访问
 */
export const canAccessMenu = (menuPath) => {
  const userStore = useUserStore()
  const userMenus = userStore.menus || []
  
  if (!menuPath) return false
  
  // 超级管理员可以访问所有菜单
  if (userStore.isSuperAdmin) return true
  
  // 递归检查菜单权限
  const checkMenuAccess = (menus, path) => {
    for (const menu of menus) {
      if (menu.path === path) {
        return true
      }
      if (menu.children && menu.children.length > 0) {
        if (checkMenuAccess(menu.children, path)) {
          return true
        }
      }
    }
    return false
  }
  
  return checkMenuAccess(userMenus, menuPath)
}

4. 按钮权限检查

/**
 * 检查用户是否可以使用指定按钮
 * @param {string} buttonCode - 按钮权限码
 * @param {string} menuPath - 所属菜单路径(可选)
 * @returns {boolean} 是否可以使用
 */
export const canUseButton = (buttonCode, menuPath = null) => {
  const userStore = useUserStore()
  
  if (!buttonCode) return false
  
  // 超级管理员可以使用所有按钮
  if (userStore.isSuperAdmin) return true
  
  // 检查按钮权限
  const buttonPermissions = userStore.buttonPermissions || []
  
  if (menuPath) {
    // 检查特定菜单下的按钮权限
    const menuButtons = buttonPermissions.filter(bp => bp.menuPath === menuPath)
    return menuButtons.some(mb => mb.code === buttonCode)
  } else {
    // 检查全局按钮权限
    return buttonPermissions.some(bp => bp.code === buttonCode)
  }
}

5. 数据权限检查

/**
 * 检查用户对数据的操作权限
 * @param {string} action - 操作类型 'view'|'add'|'edit'|'delete'
 * @param {Object} data - 数据对象
 * @param {string} resource - 资源类型
 * @returns {boolean} 是否有权限
 */
export const hasDataPermission = (action, data, resource) => {
  const userStore = useUserStore()
  
  if (!action || !resource) return false
  
  // 超级管理员拥有所有数据权限
  if (userStore.isSuperAdmin) return true
  
  // 检查基础权限
  const permissionCode = `${resource}:${action}`
  if (!hasPermission(permissionCode)) {
    return false
  }
  
  // 检查数据级权限
  const dataPermissions = userStore.dataPermissions || {}
  const resourcePermissions = dataPermissions[resource]
  
  if (!resourcePermissions) return true
  
  // 根据数据权限规则检查
  switch (resourcePermissions.scope) {
    case 'all':
      // 全部数据权限
      return true
    case 'dept':
      // 部门数据权限
      return data.departmentId === userStore.departmentId
    case 'self':
      // 个人数据权限
      return data.createBy === userStore.userId || data.userId === userStore.userId
    case 'custom':
      // 自定义数据权限
      return checkCustomDataPermission(data, resourcePermissions.rules)
    default:
      return false
  }
}

6. 自定义数据权限检查

/**
 * 检查自定义数据权限规则
 * @param {Object} data - 数据对象
 * @param {Array} rules - 权限规则数组
 * @returns {boolean} 是否有权限
 */
const checkCustomDataPermission = (data, rules) => {
  if (!rules || rules.length === 0) return false
  
  const userStore = useUserStore()
  
  return rules.some(rule => {
    switch (rule.type) {
      case 'field_equal':
        return data[rule.field] === rule.value
      case 'field_in':
        return rule.values.includes(data[rule.field])
      case 'user_field':
        return data[rule.field] === userStore[rule.userField]
      case 'dept_tree':
        return isInDepartmentTree(data.departmentId, userStore.departmentId)
      default:
        return false
    }
  })
}

7. 部门树权限检查

/**
 * 检查是否在部门树权限范围内
 * @param {number} dataDeptId - 数据所属部门ID
 * @param {number} userDeptId - 用户所属部门ID
 * @returns {boolean} 是否在权限范围内
 */
const isInDepartmentTree = (dataDeptId, userDeptId) => {
  const userStore = useUserStore()
  const departmentTree = userStore.departmentTree || []
  
  // 递归查找部门树
  const findDeptInTree = (deptId, tree) => {
    for (const dept of tree) {
      if (dept.id === deptId) {
        return dept
      }
      if (dept.children && dept.children.length > 0) {
        const found = findDeptInTree(deptId, dept.children)
        if (found) return found
      }
    }
    return null
  }
  
  // 获取用户部门节点
  const userDept = findDeptInTree(userDeptId, departmentTree)
  if (!userDept) return false
  
  // 检查数据部门是否在用户部门的子树中
  const isInSubTree = (targetId, node) => {
    if (node.id === targetId) return true
    
    if (node.children && node.children.length > 0) {
      return node.children.some(child => isInSubTree(targetId, child))
    }
    
    return false
  }
  
  return isInSubTree(dataDeptId, userDept)
}

权限指令

1. v-auth指令

/**
 * 权限指令 - 根据权限显示/隐藏元素
 * 使用方式: v-auth="'user:add'" 或 v-auth="['user:add', 'user:edit']"
 */
export const authDirective = {
  mounted(el, binding) {
    const { value } = binding
    
    if (!hasPermission(value)) {
      // 移除元素
      el.parentNode && el.parentNode.removeChild(el)
    }
  },
  updated(el, binding) {
    const { value } = binding
    
    if (!hasPermission(value)) {
      el.style.display = 'none'
    } else {
      el.style.display = ''
    }
  }
}

2. v-role指令

/**
 * 角色指令 - 根据角色显示/隐藏元素
 * 使用方式: v-role="'admin'" 或 v-role="['admin', 'manager']"
 */
export const roleDirective = {
  mounted(el, binding) {
    const { value } = binding
    
    if (!hasRole(value)) {
      el.parentNode && el.parentNode.removeChild(el)
    }
  },
  updated(el, binding) {
    const { value } = binding
    
    if (!hasRole(value)) {
      el.style.display = 'none'
    } else {
      el.style.display = ''
    }
  }
}

路由守卫集成

1. 路由权限检查

/**
 * 检查路由访问权限
 * @param {Object} to - 目标路由
 * @param {Object} from - 来源路由
 * @returns {boolean|string} true表示允许访问,string表示重定向路径
 */
export const checkRoutePermission = (to, from) => {
  const userStore = useUserStore()
  
  // 未登录用户
  if (!userStore.isLoggedIn) {
    // 白名单路由
    const whiteList = ['/login', '/register', '/forgot-password']
    if (whiteList.includes(to.path)) {
      return true
    }
    return '/login'
  }
  
  // 已登录用户访问登录页,重定向到首页
  if (to.path === '/login') {
    return '/'
  }
  
  // 检查菜单权限
  if (!canAccessMenu(to.path)) {
    return '/403'
  }
  
  // 检查路由元信息中的权限要求
  if (to.meta && to.meta.permissions) {
    if (!hasPermission(to.meta.permissions)) {
      return '/403'
    }
  }
  
  // 检查角色要求
  if (to.meta && to.meta.roles) {
    if (!hasRole(to.meta.roles)) {
      return '/403'
    }
  }
  
  return true
}

2. 动态路由生成

/**
 * 根据用户权限生成动态路由
 * @param {Array} routes - 路由配置
 * @returns {Array} 过滤后的路由
 */
export const generateAccessibleRoutes = (routes) => {
  const userStore = useUserStore()
  
  const filterRoutes = (routes) => {
    return routes.filter(route => {
      // 检查路由权限
      if (route.meta && route.meta.permissions) {
        if (!hasPermission(route.meta.permissions)) {
          return false
        }
      }
      
      // 检查角色权限
      if (route.meta && route.meta.roles) {
        if (!hasRole(route.meta.roles)) {
          return false
        }
      }
      
      // 递归过滤子路由
      if (route.children && route.children.length > 0) {
        route.children = filterRoutes(route.children)
      }
      
      return true
    })
  }
  
  return filterRoutes(routes)
}

组合式API

1. useAuth组合函数

/**
 * 权限相关的组合式API
 * @returns {Object} 权限相关的方法和状态
 */
export const useAuth = () => {
  const userStore = useUserStore()
  
  return {
    // 状态
    isLoggedIn: computed(() => userStore.isLoggedIn),
    isSuperAdmin: computed(() => userStore.isSuperAdmin),
    userInfo: computed(() => userStore.userInfo),
    permissions: computed(() => userStore.permissions),
    roles: computed(() => userStore.roles),
    
    // 方法
    hasPermission,
    hasRole,
    canAccessMenu,
    canUseButton,
    hasDataPermission,
    
    // 登录登出
    login: userStore.login,
    logout: userStore.logout,
    
    // 权限刷新
    refreshPermissions: userStore.refreshPermissions
  }
}

2. usePermission组合函数

/**
 * 权限检查的组合式API
 * @param {string|Array} permission - 权限标识
 * @returns {Object} 权限状态和方法
 */
export const usePermission = (permission) => {
  const hasAuth = computed(() => hasPermission(permission))
  
  return {
    hasAuth,
    checkPermission: (perm) => hasPermission(perm || permission)
  }
}

工具函数

1. 权限码生成

/**
 * 生成权限码
 * @param {string} module - 模块名
 * @param {string} action - 操作名
 * @returns {string} 权限码
 */
export const generatePermissionCode = (module, action) => {
  return `${module}:${action}`
}

2. 权限树构建

/**
 * 构建权限树结构
 * @param {Array} permissions - 权限列表
 * @returns {Array} 权限树
 */
export const buildPermissionTree = (permissions) => {
  const tree = []
  const map = {}
  
  // 创建映射
  permissions.forEach(permission => {
    map[permission.id] = { ...permission, children: [] }
  })
  
  // 构建树结构
  permissions.forEach(permission => {
    if (permission.parentId === 0) {
      tree.push(map[permission.id])
    } else {
      const parent = map[permission.parentId]
      if (parent) {
        parent.children.push(map[permission.id])
      }
    }
  })
  
  return tree
}

3. 权限比较

/**
 * 比较两个权限集合的差异
 * @param {Array} oldPermissions - 旧权限集合
 * @param {Array} newPermissions - 新权限集合
 * @returns {Object} 权限变化信息
 */
export const comparePermissions = (oldPermissions, newPermissions) => {
  const oldSet = new Set(oldPermissions)
  const newSet = new Set(newPermissions)
  
  const added = newPermissions.filter(p => !oldSet.has(p))
  const removed = oldPermissions.filter(p => !newSet.has(p))
  const unchanged = oldPermissions.filter(p => newSet.has(p))
  
  return {
    added,
    removed,
    unchanged,
    hasChanges: added.length > 0 || removed.length > 0
  }
}

使用示例

1. 在组件中使用

<template>
  <div>
    <!-- 使用指令 -->
    <el-button v-auth="'user:add'" @click="addUser">新增用户</el-button>
    <el-button v-role="'admin'" @click="deleteUser">删除用户</el-button>
    
    <!-- 使用组合函数 -->
    <el-button v-if="hasAuth" @click="editUser">编辑用户</el-button>
  </div>
</template>

<script setup>
import { useAuth, usePermission } from '@/common/isAuth'

const { hasPermission, hasRole } = useAuth()
const { hasAuth } = usePermission('user:edit')

const addUser = () => {
  if (hasPermission('user:add')) {
    // 执行新增操作
  }
}

const deleteUser = () => {
  if (hasRole('admin')) {
    // 执行删除操作
  }
}
</script>

2. 在路由中使用

import { checkRoutePermission } from '@/common/isAuth'

router.beforeEach((to, from, next) => {
  const result = checkRoutePermission(to, from)
  
  if (result === true) {
    next()
  } else {
    next(result)
  }
})

注意事项

  1. 权限缓存: 合理缓存权限信息,避免频繁请求
  2. 权限粒度: 设计合适的权限粒度,避免过于复杂
  3. 安全性: 前端权限检查仅用于UI控制,后端必须进行权限验证
  4. 性能优化: 权限检查函数应该高效,避免复杂计算
  5. 错误处理: 权限检查失败时提供友好的错误提示

相关文档


最后更新时间:2025-09-19