news 2026/1/22 18:35:43

管理系统权限管理(菜单、页面、按钮)react+redux/vue3 pinia实现方式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
管理系统权限管理(菜单、页面、按钮)react+redux/vue3 pinia实现方式

一、Vue3 + Pinia 权限控制示例

1️⃣ 权限点接口示例(后端返回)

{ "token": "xxx", "perms": [ "user:list", "user:add", "order:view" ] }

2️⃣ Pinia Store (stores/permission.ts)

import { defineStore } from 'pinia' interface UserData { id: string; name: string; role: string; } interface PermissionState { token: string userData: UserData | null perms: string[] } export const usePermissionStore = defineStore('permission', { state: (): PermissionState => ({ token: '', userData: null, perms: [] }), actions: { setToken(token: string) { this.token = token }, setUserData(user: UserData) { this.userData = user }, setPerms(perms: string[]) { this.perms = perms }, clear() { this.token = '' this.userData = null this.perms = [] }, // 根据 perms 过滤路由 filterRoutes(routes: any[]) { return routes.filter(route => { if (!route.meta?.perms) return true return route.meta.perms.some((p: string) => this.perms.includes(p)) }).map(route => { if (route.children) { route.children = this.filterRoutes(route.children) } return route }) }, // 菜单生成 generateMenus(routes: any[]) { return this.filterRoutes(routes) } } })

路由配置示例

export const asyncRoutes = [ { path: '/user', name: 'User', component: () => import('@/views/user/index.vue'), meta: { title: '用户管理', perms: ['user:list'] }, children: [ { path: 'add', name: 'UserAdd', component: () => import('@/views/user/add.vue'), meta: { title: '新增用户', perms: ['user:add'] } } ] }, { path: '/order', name: 'Order', component: () => import('@/views/order/index.vue'), meta: { title: '订单管理', perms: ['order:view'] } } ]

登录后调用

import { usePermissionStore } from '@/stores/permission' const permissionStore = usePermissionStore() const { token, userData, perms } = await api.login() permissionStore.setToken(token) permissionStore.setUserData(userData) permissionStore.setPerms(perms) // 生成菜单 const menuList = permissionStore.generateMenus(asyncRoutes)

按钮权限判断

<template> <button v-if="hasPerm('user:add')">新增用户</button> </template> <script setup lang="ts"> import { usePermissionStore } from '@/stores/permission' const permissionStore = usePermissionStore() function hasPerm(code: string) { return permissionStore.perms.includes(code) } </script>

二、React + Redux Toolkit 权限控制示例

Redux Slice (store/permissionSlice.ts)

import { createSlice, PayloadAction } from '@reduxjs/toolkit' interface UserData { id: string name: string role: string } interface PermissionState { token: string userData: UserData | null perms: string[] } const initialState: PermissionState = { token: '', userData: null, perms: [] } const permissionSlice = createSlice({ name: 'permission', initialState, reducers: { setToken: (state, action: PayloadAction<string>) => { state.token = action.payload }, setUserData: (state, action: PayloadAction<UserData>) => { state.userData = action.payload }, setPerms: (state, action: PayloadAction<string[]>) => { state.perms = action.payload }, clear: (state) => { state.token = '' state.userData = null state.perms = [] } } }) export const { setToken, setUserData, setPerms, clear } = permissionSlice.actions export default permissionSlice.reducer

Redux Store (store/index.ts)

import { configureStore } from '@reduxjs/toolkit' import permissionReducer from './permissionSlice' export const store = configureStore({ reducer: { permission: permissionReducer } }) export type RootState = ReturnType<typeof store.getState> export type AppDispatch = typeof store.dispatch

路由配置示例

import { lazy } from 'react' export const asyncRoutes = [ { path: '/user', element: lazy(() => import('@/pages/User')), meta: { title: '用户管理', perms: ['user:list'] }, children: [ { path: 'add', element: lazy(() => import('@/pages/UserAdd')), meta: { title: '新增用户', perms: ['user:add'] } } ] }, { path: '/order', element: lazy(() => import('@/pages/Order')), meta: { title: '订单管理', perms: ['order:view'] } } ]

登录后过滤路由

import { useSelector } from 'react-redux' import { RootState } from './store' const perms = useSelector((state: RootState) => state.permission.perms) function filterRoutes(routes: any[], perms: string[]) { return routes.filter(route => { if (!route.meta?.perms) return true return route.meta.perms.some((p: string) => perms.includes(p)) }).map(route => { if (route.children) route.children = filterRoutes(route.children, perms) return route }) } const allowedRoutes = filterRoutes(asyncRoutes, perms)

菜单渲染 & 按钮权限

// 菜单渲染 <Menu> {allowedRoutes.map(r => ( <Menu.Item key={r.path}>{r.meta.title}</Menu.Item> ))} </Menu> // 按钮权限 {perms.includes('user:add') && <Button>新增用户</Button>}

核心思路(Vue / React 通用)

  1. 后端只返回 perms 数组 → 前端根据它控制 UI
  2. 前端菜单 / 路由根据 perms 过滤
  3. 按钮或页面模块用 perms.includes(…) 控制显示
  4. 后端接口 仍然要校验 perms → 真正的安全保障

菜单写死在前端,通过权限点 v-if 显示/隐藏(大厂常用)

流程:

  1. 菜单是前端写死的(存 routes.ts)
  2. 用户登录,后台只返回权限点(字符串数组,如 [‘user:add’, ‘user:delete’])
  3. 前端根据权限点过滤导航栏、路由
  • 示例
{ path: '/user', name: 'User', meta: { title: '用户管理', perms: ['user:view'] } }

✔ 优点:

  1. 前端掌控路由、菜单,灵活更容易维护
  2. 不需要后端维护菜单表
  3. 常用于大厂 组件化、前后端分仓 的模式
  4. 权限点即可驱动显示/隐藏按钮

前端菜单只是“显示控制”,不是安全控制

就算你用后台动态返回菜单,也不能防止用户:

  1. 手动输入地址
  2. 用 F12 改源码
  3. 注入 JS
  4. 抓包直接调接口

真正的安全是 API 权限验证

所以前端写死菜单 + 权限过滤 = 大厂最佳体验

措施目的
前端菜单过滤体验层:让用户不看到没权限的东西
后端接口校验安全层:真正的权限防护(不可绕过)

后端控制实际操作

  1. 所有 API 请求都要校验用户权限
  2. 后端拿 token → 查角色 → 查 perms → 判断是否允许该操作
  3. 例如:POST /api/user/add
    • 用户 perms 包含 user:add → 允许
    • 用户 perms 不包含 user:add → 返回 403 Forbidden

重点

  1. 前端 不需要 每次请求都传 user:add
  2. 后端 通过 token 或 session 自动知道 谁在操作,然后根据权限码判断是否允许
  3. 前端只是可视化限制,真正的安全由后端负责

最终总结(Vue + React 通用)

功能点Vue3 实现React 实现
菜单写死asyncRoutesroutes.tsx
权限点存储PiniaZustand/Redux
菜单过滤filterRoutes()filterRoutes()
按钮权限v-if hasPerm()perms.includes()
安全关键必须后端接口校验必须后端接口校验

另外一种方案

菜单从后台返回(动态菜单),超级管理员添加菜单,登录后通过用户权限返回菜单的方式
流程:

  1. 超级管理员在后台添加菜单
  2. 菜单存数据库
  3. 用户登录 → 后端根据权限返回“可访问菜单树”
  4. 前端根据菜单渲染侧边栏、路由
    ✔ 优点:
  • 菜单可配置,前端不用改代码
  • 多系统保持一致
  • 超级管理员可随时调整菜单、权限
    ✘ 缺点:
  • 需要后端写复杂的“菜单树 + 角色过滤”
  • 前端路由也要动态生成
  • 有些公司根本不想把菜单配到数据库里
    现在已经放弃这个方案了
  • 大厂前端团队多,菜单都是前端组件结构
  • 菜单不需要天天变,写死才稳定
  • 权限只是控制菜单是否渲染,不需要写数据表
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/22 11:49:39

闪电AI文档转换Lite:离线免费的全能文档处理神器

闪电AI文档转换Lite&#xff1a;离线免费的全能文档处理神器 【免费下载链接】convert-lite flashai-convert-lite&#xff0c;离线免费文档转换工具&#xff0c;支持pdf to markdown,word to markdown,excel to markdown,ppt to markdown, html to markdown,image to markdown…

作者头像 李华
网站建设 2026/1/20 15:41:54

Windows系统pgvector一键部署攻略:告别编译烦恼,轻松开启向量搜索

还在为pgvector在Windows上的安装而头疼吗&#xff1f;&#x1f914; 别担心&#xff0c;今天我带你绕开所有坑点&#xff0c;用最简单的方式在Windows系统上成功部署这个强大的PostgreSQL向量搜索扩展&#xff01;无论你是AI开发者还是数据库爱好者&#xff0c;都能在10分钟内…

作者头像 李华
网站建设 2026/1/20 19:17:38

伊朗地毯数据集,波斯地毯Lechak-Toranj和Afshan图案分类,计算机视觉机器学习训练,纺织设计分析增强样本,装饰艺术特征提取对称检测算法,纹理分析Gabor滤波,个性化定制图案生成

伊朗地毯数据集&#xff0c;波斯地毯Lechak-Toranj和Afshan图案分类&#xff0c;计算机视觉机器学习训练&#xff0c;纺织设计分析增强样本&#xff0c;装饰艺术特征提取对称检测算法&#xff0c;纹理分析Gabor滤波&#xff0c;个性化定制图案生成 在传统艺术文化遗产保护、纺…

作者头像 李华
网站建设 2026/1/20 12:47:20

[基础算法学习]backtrack回溯法(三):从N皇后、解数独带你掌握棋盘回溯问题

[基础算法学习]backtrack回溯法(三):从N皇后、解数独带你掌握棋盘回溯问题 在回溯法(一)和回溯法(二)两篇文章中&#xff0c;介绍了回溯法的万能模版以及树枝去重、树层去重剪枝技巧。本文将继续讲解回溯法中较难一类问题——棋盘问题&#xff0c;并通过N皇后和 解数独两道经典…

作者头像 李华
网站建设 2026/1/20 20:00:02

Go之路 - 7.go的函数

一、函数基础 1.1 函数声明 // 基本语法 func 函数名(参数列表) (返回值列表) {// 函数体 }// 示例 func add(x int, y int) int {return x y }// 简化参数类型&#xff08;相同类型&#xff09; func multiply(x, y int) int {return x * y }1.2 多返回值 // 返回多个值 func…

作者头像 李华