当前位置: 首页 > news >正文

后台管理系统权限管理:前端实现详解

需求背景:
( 公司希望在现运行的后台系统上, 满足不同角色的划分,保护隐私数据 ,实现权限控制, 于是我基于诺依系统的权限管理功能, 借鉴了思路)

  • 数据隔离:按角色划分数据与功能可见范围,如普通员工无权查看管理层报表。​
  • 操作限制:基于角色职责管控按钮级操作,如禁止实习生执行删除、审批等高危操作。​
  • 体验优化:动态隐藏无权限的菜单、按钮,减少无效操作,降低学习成本。​
  • 路由安全:拦截通过手动修改 URL 访问无权限页面的行为,避免前端报错和后端无效请求。

项目实现部分截图

 

一、权限数据的初始化流程

前端权限系统的运转始于权限数据的加载,这一过程集中在用户登录后的初始化阶段。诺依前端采用 Vue 生态,通过 Vuex 管理全局状态,权限数据的流转路径清晰可控。

1. 登录成功后的权限加载触发​

当用户登录请求得到后端响应(包含 token)后,前端会立即调用权限初始化方法:

// src/store/modules/user.js
actions: {// 登录动作login({ commit }, userInfo) {return new Promise((resolve, reject) => {login(userInfo).then(response => {const { token } = response.data;// 存储tokensetToken(token);commit('SET_TOKEN', token);resolve();}).catch(error => {reject(error);});});},// 加载用户权限信息getInfo({ commit, state }) {return new Promise((resolve, reject) => {getInfo().then(response => {const { user, roles, permissions } = response.data;// 存储用户信息、角色、权限标识commit('SET_USER', user);commit('SET_ROLES', roles);commit('SET_PERMISSIONS', permissions);resolve(response.data);}).catch(error => {reject(error);});});}
}

这里的getInfo接口会返回三组关键数据:用户基本信息、角色列表(如["admin", "user"])、权限标识集合(如["system:user:add", "system:user:edit"])。

2. 动态路由的生成与注入

权限标识加载完成后,下一步是生成可访问的路由配置。诺依将路由分为 “常量路由”(如登录页、404 页)和 “动态路由”(需权限控制的业务页面):

// src/store/modules/permission.js
actions: {generateRoutes({ commit, state }) {return new Promise(resolve => {// 调用后端接口获取菜单树getRouters().then(response => {const asyncRoutes = response.data;// 将后端返回的菜单数据转换为前端路由配置const accessedRoutes = filterAsyncRoutes(asyncRoutes);commit('SET_ROUTES', accessedRoutes);resolve(accessedRoutes);});});}
}// 菜单转路由的核心函数
function filterAsyncRoutes(routes) {const res = [];routes.forEach(route => {const tmp = { ...route };// 组件路径转换(诺依约定组件路径格式为"module/component")if (tmp.component) {// 目录if (tmp.component === 'Layout') {tmp.component = () => import("@/views/index");; // 父级菜单} else if (tmp.component === "ParentView") {tmp.component = ParentView;// 子菜单}else {tmp.component = loadView(tmp.component); // 懒加载业务组件}}// 递归处理子菜单if (tmp.children && tmp.children.length > 0) {tmp.children = filterAsyncRoutes(tmp.children);}res.push(tmp);});return res;
}// 组件懒加载函数
export const loadView = (view) => {return () => import(`@/views/${view}`);
};

转换完成的路由会通过router.addRoutes方法注入 Vue Router,此时用户才能通过路由访问对应页面。

二、路由守卫:权限校验

即使动态路由已注入,仍需防止用户通过手动修改 URL 访问无权限页面。诺依通过全局路由守卫实现实时权限校验:

// src/permission.js
router.beforeEach(async(to, from, next) => {// 从本地存储获取tokenconst hasToken = getToken();if (hasToken) {if (to.path === '/login') {// 已登录跳转首页next({ path: '/' });} else {// 判断是否已加载权限信息const hasRoles = store.getters.roles && store.getters.roles.length > 0;if (hasRoles) {next();} else {try {// 加载权限信息const { roles } = await store.dispatch('user/getInfo');// 生成动态路由const accessRoutes = await store.dispatch('permission/generateRoutes');// 注入路由router.addRoutes(accessRoutes);// 重定向至目标路由(确保路由已生效)next({ ...to, replace: true });} catch (error) {// 权限加载失败,清除token并跳转登录页await store.dispatch('user/resetToken');Message.error(error || '权限加载失败');next(`/login?redirect=${to.path}`);}}}} else {// 未登录状态处理if (whiteList.indexOf(to.path) !== -1) {// 白名单路由直接放行next();} else {// 非白名单路由跳转登录页next(`/login?redirect=${to.path}`);}}
});

这段代码构成了权限校验的核心逻辑:未登录用户强制跳转登录页,已登录用户需完成权限加载与路由注入后才能访问页面。

三、动态菜单渲染:从数据到 UI 的转换

菜单是权限最直观的体现,诺依通过递归组件将权限菜单数据转换为可视化导航:​

1. 菜单组件的递归渲染

<!-- src/layout/components/Sidebar/SidebarMenu.vue -->
<template><div class="menu-container"><el-menu:default-active="activeMenu":collapse="isCollapse"mode="vertical"><template v-for="(route, index) in routes"><!-- 有子菜单的项 --><el-submenuv-if="route.children && route.children.length > 0":index="route.path":key="index"><template #title><svg-icon :icon-class="route.icon" /><span>{{ route.meta.title }}</span></template><!-- 递归渲染子菜单 --><sidebar-menu:routes="route.children":base-path="resolvePath(route.path)":is-collapsed="isCollapse"/></el-submenu><!-- 无子菜单的项 --><el-menu-itemv-else:index="route.path":key="index"@click="handleClick(route)"><svg-icon :icon-class="route.icon" /><span>{{ route.meta.title }}</span></el-menu-item></template></el-menu></div>
</template><script>
export default {name: 'SidebarMenu',components: { SidebarMenu }, // 递归组件注册props: {routes: { type: Array, required: true },basePath: { type: String, default: '' },isCollapsed: { type: Boolean, default: false }},methods: {resolvePath(path) {return this.basePath ? `${this.basePath}/${path}` : path;},handleClick(route) {// 菜单点击事件处理this.$router.push(route.path);}},computed: {activeMenu() {// 计算当前激活的菜单const route = this.$route;return route.path;}}
};
</script>

通过递归调用sidebar-menu组件,无论菜单层级有多深,都能被正确渲染为嵌套的下拉菜单。

四、按钮级权限控制:指令化实现方案

诺依通过自定义 Vue 指令v-permission实现按钮级别的权限控制,核心逻辑是 “权限校验 + DOM 操作”:​

1. 权限指令的实现

// src/directive/permission/index.js
import Vue from 'vue';
import { hasPermission } from '@/utils/permission';// 注册v-permission指令
Vue.directive('permission', {inserted(el, binding, vnode) {const { value } = binding;// 权限标识可以是字符串或数组if (value && value instanceof Array && value.length > 0) {const hasAuth = hasPermission(value);if (!hasAuth) {// 无权限则移除元素el.parentNode && el.parentNode.removeChild(el);}} else {throw new Error('v-permission指令需要传入权限标识数组,如v-permission="[\'system:user:add\']"');}}
});// 权限校验工具函数
// src/utils/permission.js
export function hasPermission(permissions) {// 从Vuex获取用户拥有的权限集合const userPermissions = store.getters && store.getters.permissions;// 判断用户是否拥有目标权限(支持多权限“或”关系)return permissions.some(permission => userPermissions.includes(permission));
}

2. 指令在页面中的使用​

在业务组件中,只需为按钮添加v-permission指令并传入所需权限标识,即可实现权限控制:

<!-- src/views/system/user/index.vue -->
<template><el-button type="primary" @click="handleAdd" v-permission="['system:user:add']"><i class="el-icon-plus"></i> 新增用户</el-button><el-table-column label="操作" width="200"><template #default="scope"><el-button size="mini" @click="handleEdit(scope.row)" v-permission="['system:user:edit']">编辑</el-button><el-button size="mini" type="danger" @click="handleDelete(scope.row)" v-permission="['system:user:delete']">删除</el-button></template></el-table-column>
</template>

当用户无对应权限时,按钮会在组件初始化阶段被从 DOM 中移除,实现 “无权限则不可见” 的效果。

五、前端权限优化:缓存与动态更新

在实际应用中,权限可能会在用户使用过程中发生变更(如管理员修改了角色权限)。诺依通过以下方式优化权限动态性:​

1. 权限缓存策略​

  • 基础权限数据(角色、权限标识)存储在 Vuex 中,同时备份到sessionStorage,避免页面刷新后权限丢失;​
  • 动态路由信息仅存储在 Vue Router 实例中,刷新页面后会重新调用generateRoutes接口生成。​

2. 权限动态更新​

当权限发生变更时,前端可通过调用reloadPermission方法强制刷新权限:

// 权限刷新方法
export function reloadPermission() {// 清除Vuex中的权限数据store.dispatch('user/resetInfo');// 重新加载权限信息store.dispatch('user/getInfo').then(() => {// 重新生成路由store.dispatch('permission/generateRoutes').then(routes => {// 重置路由并注入新路由router.matcher = new VueRouter({ mode: 'history' }).matcher;router.addRoutes(routes);// 刷新当前页面router.go(0);});});
}

这种方式虽然会导致页面刷新,但能确保权限变更立即生效,适合权限调整不频繁的场景。​

总结:前端权限的核心设计思想

诺依前端权限管理的实现,本质是 **“数据驱动的 UI 适配”**:通过后端返回的权限数据,前端动态调整路由配置、菜单结构和按钮显隐,同时通过路由守卫和后端校验构建双重安全机制。​

其设计亮点在于:​

  • 指令化控制:将权限判断逻辑封装为指令,降低业务代码与权限逻辑的耦合;​
  • 递归组件:通过递归实现任意层级菜单的渲染,适应复杂的权限结构;​
  • 懒加载策略:路由组件和权限数据按需加载,平衡性能与安全性。​

对于开发者而言,理解这些实现细节不仅能快速定位权限相关问题,更能在自定义权限系统时,借鉴其 “前端做展示控制,后端做安全校验” 的设计原则,构建既灵活又安全的权限体系。

http://www.lryc.cn/news/607548.html

相关文章:

  • PDFsam免费开源!PDF分割合并工具
  • unity学习——视觉小说开发(一)
  • AI应用UX设计:让技术更懂用户
  • Android Jetpack 系列(五)Room 本地数据库实战详解
  • 第一个大语言模型的微调
  • Transformer架构全解析:搭建AI的“神经网络大厦“
  • Spring之【循环引用】
  • 插件升级:Chat/Builder 合并,支持自定义 Agent、MCP、Rules
  • 小学阶段的学习机推荐:科大讯飞T30、Lumie 10学习机暑期16项AI功能升级
  • 代码随想录day52图论3
  • Effective C++ 条款15:在资源管理类中提供对原始资源的访问
  • ICML 2025 | 深度剖析时序 Transformer:为何有效,瓶颈何在?
  • qt中的手势
  • STM32学习记录--Day5
  • 操作系统-lecture4(进程的调度)
  • win10 VC++6.0 应用程序无法正常运行 0xc0000142,应用程序无法正常启动,报错“0xc0000142”,解决办法
  • 深度解读 CSGHub:开源协议、核心功能与产品定位
  • Springboot 配置 doris 连接
  • Spring Boot 异步执行方式全解析:@Async、CompletableFuture 与 TaskExecutor 对比
  • JavaWeb笔记2-JavaScriptVueAjax
  • 备案主体更换期间网站可以访问吗
  • opencv-python的GPU调用
  • 树莓派GPIO介绍 + LED控制
  • 智能Agent场景实战指南 Day 28:Agent成本控制与商业模式
  • xcode swift项目运行、连接真机运行报错,引入文件夹失败
  • [2025CVPR-图象生成方向]ODA-GAN:由弱监督学习辅助的正交解耦比对GAN 虚拟免疫组织化学染色
  • python PIL图片转base64字符串
  • 练习javaweb+mysql+jsp
  • 告别“AI味”图像!最新开源AI模型FLUX.1-Krea实现真实光影生成
  • [CISCN 2022 初赛]online_crt