前端组件级权限控制
组件级权限控制的核心思路是:先统一管理权限数据,再通过 "声明式" 或 "编程式" 的方式,在组件层面做权限校验。下面我会从核心方案、具体实现、最佳实践三个维度,给你讲清楚落地方式。
一、前置准备:权限数据的统一管理
无论哪种控制方式,第一步都是先把用户的权限数据规整好,这是所有组件权限控制的基础:
1、权限数据格式:
javascript
// 登录后从后端获取的用户权限数据(示例)
const userPermissions = {
role: 'admin', // 角色:admin/editor/guest
permissions: \['user:add', 'user:edit', 'user:delete', 'menu:report'], // 细粒度权限码
menus: \['dashboard', 'user', 'report'] // 可访问的菜单标识
};
2、权限数据存储:
-
存入 Vuex/Pinia/Redux 等状态管理库(全局可访问);
-
配合 localStorage/sessionStorage 做持久化(避免页面刷新丢失)。
二、组件级权限控制的 3 种核心方案(从易到难)
方案 1:指令式控制(推荐,适合按钮 / 小组件)
通过自定义指令(如v-permission),直接在组件标签上声明所需权限,无权限则隐藏 / 禁用组件,是最常用的方式。
以 Vue3 为例实现自定义权限指令:
javascript
// src/directives/permission.js
import { useUserStore } from '@/stores/user'; // 引入状态管理的权限数据
// 自定义权限指令
export const permissionDirective = {
mounted(el, binding) {
const userStore = useUserStore();
const requiredPerms = binding.value; // 获取指令传入的权限码(如\['user:delete'])
// 校验权限:用户权限列表是否包含所需权限
const hasPermission = requiredPerms.some(perm =>
userStore.permissions.includes(perm)
);
// 无权限则移除组件(或禁用,根据需求调整)
if (!hasPermission) {
el.parentNode?.removeChild(el); // 彻底移除
// 也可选择禁用:el.disabled = true; el.style.opacity = 0.5;
}
}
};
// 全局注册指令(main.js)
import { createApp } from 'vue';
import { permissionDirective } from '@/directives/permission';
const app = createApp(App);
app.directive('permission', permissionDirective);
使用方式(组件中):
javascript
<template>
<!-- 单个权限控制 -->
<el-button v-permission="\['user:delete']">删除用户\</el-button>
<!-- 多个权限满足其一即可 -->
<el-button v-permission="\['user:add', 'admin']">新增/管理员专属\</el-button>
</template>
当然这是个侵入式的,会修改组件的内容。如果不想侵入,修改组件则可以使用方式2;
方案 2:组件封装式控制(适合通用权限组件)
封装一个<Permission>容器组件,内部做权限校验,只有满足权限才渲染子组件,适合复用性高的场景。
封装权限组件(Vue3):
src/components/Permission.vue
javascript
<template>
<slot v-if="hasPermission"></slot>
</template>
<script setup>
import { useUserStore } from '@/stores/user';
import { computed } from 'vue';
const props = defineProps({
// 所需权限码/角色
perms: {
type: Array,
required: true
}
});
const userStore = useUserStore();
// 计算是否有权限
const hasPermission = computed(() => {
return props.perms.some(perm =>
userStore.permissions.includes(perm) || userStore.role === perm
);
});
</script>
使用方式:
javascript
<template>
<Permission perms="\['user:edit']">
<el-button>编辑用户\</el-button>
</Permission>
<!-- 角色控制 -->
<Permission perms="\['admin']">
<el-menu-item>管理员专属菜单\</el-menu-item>
</Permission>
</template>
方案 3:编程式控制(适合复杂逻辑)
在组件的 JS 逻辑中直接判断权限,控制组件的显示 / 隐藏或功能执行,适合权限逻辑复杂的场景(如结合多个条件)。
示例(Vue3):
javascript
<template>
<el-button @click="handleDelete" v-if="canDelete">删除\</el-button>
</template>
<script setup>
import { useUserStore } from '@/stores/user';
import { computed } from 'vue';
const userStore = useUserStore();
// 编程式判断权限
const canDelete = computed(() => {
// 复杂逻辑:管理员 且 有删除权限 且 不是只读模式
return userStore.role === 'admin'
&& userStore.permissions.includes('user:delete')
&& !userStore.readonly;
});
const handleDelete = () => {
// 再次校验(防止前端被篡改,双重保障)
if (!canDelete.value) {
ElMessage.error('无删除权限');
return;
}
// 执行删除逻辑
};
</script>
三、进阶优化:避免前端权限被篡改
前端权限控制只是 "体验层",必须配合后端校验,否则用户可通过修改前端数据绕过权限:
-
接口层面:每个敏感接口(如删除、新增)都要校验用户权限,即使前端显示了按钮,无权限的请求后端也要返回 403;
-
权限数据加密 :从后端获取的权限数据可做简单加密 / 签名,前端校验前先验签,防止手动修改
localStorage中的权限; -
路由守卫兜底:即使组件级控制失效,路由守卫也要校验权限,避免无权限用户通过 URL 直接访问敏感页面。
四、不同框架的适配
-
React :无指令概念,优先用组件封装式(如
<Permission perm="user:delete">)或 Hooks(usePermission('user:delete')); -
Angular :通过
*ngIf结合权限服务(permissionService.hasPerm('user:delete')),或自定义结构指令(类似 Vue 的自定义指令); -
原生 JS:通过 DOM 操作,先获取组件元素,再根据权限判断是否移除 / 禁用。
总结
-
核心方式:组件级权限控制优先用「自定义指令(Vue)/ 组件封装(React)」,复杂逻辑用编程式判断;
-
数据基础:先统一管理用户权限数据(角色 / 权限码),存入全局状态;
-
安全兜底:前端控制仅做体验优化,必须配合后端接口权限校验,防止篡改。