3.3 KiB
3.3 KiB
9.7 权限控制
:::tip 概述
前端权限控制通过 v-perm / v-perms 指令实现按钮级权限控制。前端权限只是体验层(隐藏按钮),真正的安全防线是后端 #[Permission] 注解 + auth.jwt 中间件。
:::
权限指令
v-perm(单权限)
<template>
<!-- 只有拥有 sys:user:add 权限的用户才能看到此按钮 -->
<el-button v-perm="['sys:user:add']" type="primary">新增</el-button>
</template>
v-perms(多权限,满足任一即可)
<template>
<!-- 拥有 sys:user:add 或 sys:user:update 任一权限即可 -->
<el-button v-perms="['sys:user:add', 'sys:user:update']">操作</el-button>
</template>
指令实现
// ui/src/directives/permission.ts
import { usePermission } from '@/hooks/web/usePermission';
export const perm: ObjectDirective = {
mounted(el: HTMLButtonElement, binding) {
const { hasSomePermission } = usePermission();
if (!hasSomePermission(binding.value)) {
el.parentNode.removeChild(el); // 无权限则移除元素(非隐藏)
}
},
};
:::tip 移除而非隐藏
无权限时元素直接从 DOM 移除(而非 display:none)——避免通过开发者工具看到"隐藏"的敏感操作按钮。
:::
usePermission Hook
:::tip 超级管理员
用户权限列表包含 *:*:* 时,表示超级管理员,拥有所有权限。
:::
// ui/src/hooks/web/usePermission.ts
export function usePermission() {
const userStore = useUserStore();
/**
* 检查是否拥有指定权限
* 超级管理员(permissions 包含 '*:*:*')拥有所有权限
*/
function hasSomePermission(permission: string | string[]): boolean {
const perms = userStore.permissions;
if (perms.includes('*:*:*')) return true;
if (typeof permission === 'string') {
return perms.includes(permission);
}
return permission.some((p) => perms.includes(p));
}
return { hasSomePermission, hasEveryPermission };
}
权限数据来源
用户登录后,GET /api/index/getUser 接口返回权限列表(IndexService::getUser → JwtService::getUserPermissions,与后端鉴权同一缓存):
{
"permissions": [
"sys:user:page",
"sys:user:add",
"sys:user:update",
"sys:user:delete",
"sys:role:page"
]
}
超级管理员返回 ["*:*:*"],拥有所有权限。
权限码对应关系
| 端 | 机制 | 示例 |
|---|---|---|
| 后端 | #[Permission('sys:user:add', '添加用户')] 注解 + 中间件反射鉴权 |
403 拦截 |
| 前端 | v-perm="['sys:user:add']" 指令 |
按钮移除 |
| 菜单 | laravel_menu.permission 字段(type=1 权限节点) |
角色树勾选 |
三处权限码必须完全一致(sys:{模块}:{操作})。
常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 按钮不显示但接口可调 | 角色有权限码但 getUser 未返回 |
重新登录刷新权限缓存 |
| 按钮显示但接口 403 | 菜单 permission 与注解不一致 | 逐字符比对权限码 |
| 超管按钮仍隐藏 | permissions 未含 *:*:* |
检查 getUser 返回(uid=1 应为通配) |
| 修改角色权限后前端无变化 | 前端权限缓存在 Store | 重新登录或刷新页面 |
详见 5.4 权限管理(RBAC)。