isAuth.js是权限验证工具模块,提供用户权限检查、角色验证、菜单权限等功能。
文件位置: src/common/isAuth.js
/**
* 检查用户是否有指定权限
* @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
}/**
* 检查用户是否有指定角色
* @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
}/**
* 检查用户是否可以访问指定菜单
* @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)
}/**
* 检查用户是否可以使用指定按钮
* @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)
}
}/**
* 检查用户对数据的操作权限
* @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
}
}/**
* 检查自定义数据权限规则
* @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
}
})
}/**
* 检查是否在部门树权限范围内
* @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)
}/**
* 权限指令 - 根据权限显示/隐藏元素
* 使用方式: 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 = ''
}
}
}/**
* 角色指令 - 根据角色显示/隐藏元素
* 使用方式: 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 = ''
}
}
}/**
* 检查路由访问权限
* @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
}/**
* 根据用户权限生成动态路由
* @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
* @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
}
}/**
* 权限检查的组合式API
* @param {string|Array} permission - 权限标识
* @returns {Object} 权限状态和方法
*/
export const usePermission = (permission) => {
const hasAuth = computed(() => hasPermission(permission))
return {
hasAuth,
checkPermission: (perm) => hasPermission(perm || permission)
}
}/**
* 生成权限码
* @param {string} module - 模块名
* @param {string} action - 操作名
* @returns {string} 权限码
*/
export const generatePermissionCode = (module, action) => {
return `${module}:${action}`
}/**
* 构建权限树结构
* @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
}/**
* 比较两个权限集合的差异
* @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
}
}<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>import { checkRoutePermission } from '@/common/isAuth'
router.beforeEach((to, from, next) => {
const result = checkRoutePermission(to, from)
if (result === true) {
next()
} else {
next(result)
}
})- 权限缓存: 合理缓存权限信息,避免频繁请求
- 权限粒度: 设计合适的权限粒度,避免过于复杂
- 安全性: 前端权限检查仅用于UI控制,后端必须进行权限验证
- 性能优化: 权限检查函数应该高效,避免复杂计算
- 错误处理: 权限检查失败时提供友好的错误提示
最后更新时间:2025-09-19