vue权限控制和动态路由

article/2025/9/18 3:43:39

思路


  • 登录:当用户填写完账号和密码后向服务端验证是否正确,验证通过之后,服务端会返回一个token,拿到token之后(我会将这个token存贮到localStore中,保证刷新页面后能记住用户登录状态),前端会根据token再去拉取一个 user_info 的接口来获取用户的详细信息(如用户权限,用户名等等信息)。
  • 权限验证:通过token获取用户对应的 role,动态根据用户的 role 算出其对应有权限的路由,通过 router.addRoutes 动态挂载这些路由。

路由定义


路由分为两种:constantRoutesasyncRoutes

constantRoutes : 代表那些不需要动态判断权限的路由,如登录页、通用页等。

asyncRoutes : 代表那些需要动态判断权限并通过addRoutes动态添加的页面。

创建router.js


import Vue from "vue";
import VueRouter from "vue-router";
import Layout from "@/layout";Vue.use(VueRouter);//通用页面:不需要守卫,可直接访问
export const constRoutes = [{path: "/login",component: () => import("@/views/Login.vue"),hidden: true //导航菜单忽略该项},{path: "/",component: Layout, //应用布局redirect: "/home",alwaysShow: true,meta: {title: "客户管理",  //导航菜单项标题icon:"kehu" //导航菜单项图标},children: [{path: "/home",component: () => import("@/views/Home.vue"),name: "home",meta: {title: "客户列表"}}]}
];//权限页面:受保护页面,要求用户登录并拥有访问权限的角色才能访问
export const asyncRoutes = [{path: "/system_manage",component: Layout,redirect: "/system_set",meta: {title: "系统设置",icon: "set"},children: [	{path: "/system_set",component: () => import("@/views/system_set.vue"),name: "system_set",meta: {title: "系统设置",roles: ["admin", "editor"] // 设置该路由进入的权限,支持多个权限叠加}},{path: "/system_organiza",component: () => import("@/views/system_origaniza.vue"),name: "system_origaniza",meta: {title: "组织结构",roles: ["admin"]},children:[//三级路由嵌套,还要手动在二级目录的根文件下添加一个 <router-view />{path:'/custom_link',name:'custom_link',component:() => import("@/views/custom_link.vue"),meta:{title:'客户联系人'}},{path:'/tracking',name:'tracking',component:() => import("@/views/tracking.vue"),meta:{title:'跟踪记录'}}]},{path: "/system_data",component: () => import("@/views/system_data.vue"),name: "system_data",meta: {title: "数据字典",roles: ["admin"]}}]}
];const router = new VueRouter({mode: "history",base: process.env.BASE_URL,routes: constRoutes
});export default router;复制代码

登录


创建登录页 views/Login.vue

<template><div class="container"><h2>用户登录</h2><input type="text" v-model="username" /><button @click="login">登录</button></div>
</template><script>
export default {data() {return {username: ""};},methods: {login() {/*this.$store.dispatch("user/login", { username: this.username }).then(() => {this.$router.push({//   接受路由参数然后跳转path: this.$route.query.redirect || "/"});}).catch(error => {alert(error);});*///调api获取token}}
};
</script>复制代码

用户登陆状态维护


vuex根模块实现,./store/index.js

import Vue from "vue";
import Vuex from "vuex";
import user from "./modules/user";
import permission from "./modules/permission";Vue.use(Vuex);export default new Vuex.Store({state: {},mutations: {},actions: {},modules: { user, permission },getters: {roles: state => {return state.user.roles;}}
});
复制代码

user模块-存储token 和 roles ./store/modules/user.js

const state = {token: localStorage.getItem("token"),roles: []
};const mutations = {SET_TOKEN: (state, token) => {state.token = token;},SET_ROLES: (state, roles) => {state.roles = roles;}
};const actions = {login({ commit }, userinfo) {const { username } = userinfo;return new Promise((resolve, reject) => {setTimeout(() => {if (username === "admin" || username === "jerry") {commit("SET_TOKEN", username);localStorage.setItem("token", username);resolve();} else {reject("用户名、密码错误");}}, 1000);});},getInfo({ commit, state }) {return new Promise(resolve => {setTimeout(() => {const roles = state.token === "admin" ? ["admin"] : ["editor"];commit("SET_ROLES", roles);resolve(roles);}, 1000);});}
};export default {namespaced: true,state,mutations,actions
};复制代码

路由守卫


创建./src/permission.js

import router from "./router";
import store from "./store";const whiteList = ["/login"]; //无需令牌白名单router.beforeEach(async (to, from, next) => {//to and from are Route Object,next() must be called to resolve the hook// 获取令牌判断用户是否登录const hasToken = localStorage.getItem("token");if (hasToken) {//已登录if (to.path === "/login") {//若以登录没有必要显示登录页,重定向回首页next({ path: "/" });} else {// 去其他路由const hasRoles =store.state.user.roles && store.state.user.roles.length > 0;if (hasRoles) {// 若用户角色已付加则说明权限以判定,动态路由已添加next();} else {try {// 请求获取用户信息const roles = await store.dispatch("user/getInfo");console.log(roles);// 根据当前用户角色动态生成路由const accessRoutes = await store.dispatch("permission/generateRoutes",roles);console.log(accessRoutes);// 添加这些至路由器router.addRoutes(accessRoutes);// 继续路由切换,确保addRoutes完成next({ ...to });} catch (error) {// 出错需要重置令牌(令牌过期,网络错误等原因)//await store.dispatch('user/resetToken')next(`/login?redirect=${to.path}`);alert(error || "未知错误");}}}} else {//未登录if (whiteList.indexOf(to.path) !== -1) {// 白名单中的路由路过next();} else {// 重定向至登录页next(`/login?redirect=${to.path}`);}}
});复制代码

添加动态路由


根据用户角色过滤出可访问路由并动态添加到router 创建permission模块,store/modules/permission.js

import { constRoutes, asyncRoutes } from "@/router";const state = {routes: [], //完整路由表addRoutes: [] //用户可访问路由表
};const mutations = {SET_ROUTES: (state, routes) => {state.addRoutes = routes;state.routes = constRoutes.concat(routes);}
};const actions = {// 路由生成:在得到用户角色后第一时间调用generateRoutes({ commit }, roles) {return new Promise(resolve => {// 根据角色做过滤处理const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles);commit("SET_ROUTES", accessedRoutes);resolve(accessedRoutes);});}
};/*** 递归过滤AsyncRoutes路由表* @routes 带过滤的路由表,首次传入的就是AsyncRoutes* @roles  用户拥有角色*/
export function filterAsyncRoutes(routes, roles) {const res = [];routes.forEach(route => {// 复制一份const tmp = { ...route };// 如果用户有访问权限则加入结果路由表if (hasPermission(roles, tmp)) {// 如果存在子路由则递归过滤之if (tmp.children) {tmp.children = filterAsyncRoutes(tmp.children, roles);}res.push(tmp);}});return res;
}/*** 根据路由meta.role确定是否当前用户拥有访问权限* @roles 用户拥有的角色* @route 待判定路由*/export function hasPermission(roles, route) {if (route.meta && route.meta.roles) {//  若用户拥有的角色中有被包含在待判定的路由角色表中则拥有访问权return roles.some(role => route.meta.roles.includes(role));} else {//  没有设置roles则无需判定即可访问return true;}
}export default {namespaced: true,state,mutations,actions
};复制代码

异步获取路由表


用户登录后向后端请求可访问的路由表,从而动态生成可访问页面,操作和原来是相同的,这里多了一步将后端返回路由表中组件名称和本地的组件映射步骤:

//前端的映射表map就是之前的asyncRoutes
//服务端返回的map类似于
const serviceMap = [{path:'/login',component:'login',hidden:true}
]
//遍历serviceMap,将component替换为map[component],动态生成asyncRoutes
function mapComponent(serviceMap){serviceMap.forEach(route => {route.component = map[route.component];if(route.children){route.children.map(child => mapComponent(child))}})
}
mapComponent(serviceMap)
复制代码

按钮权限

封装一个指令v-permission,从而实现按钮级别权限控制,创建src/directtive/permission.js

自定义指令参考 cn.vuejs.org/v2/guide/cu…

import store from "@/store";
const permission = {inserted(el, binding) {// 获取指令的值:按钮要求的角色数组const { value: pRoles } = binding;// 获取用户角色const roles = store.getters && store.getters.roles;if (pRoles && pRoles instanceof Array && pRoles.length > 0) {const hasPermission = roles.some(role => {return pRoles.includes(role);});// 如果没有权限删除当前domif (!hasPermission) {el.parentNode && el.parentNode.removeChild(el);}} else {throw new Error(`需要指定按钮要求角色数组,如v-permission="['admin','editor']"`);}}
};
export default permission;复制代码

注册指令 main.js

import vPermission from "./directive/permission";
Vue.directive("permission", vPermission);
复制代码

测试

<button v-permission="['admin', 'editor']">admin editor</button>
<button v-permission="['admin']">admin</button>
复制代码

该指令只能删除挂在指令的元素,对于那些额外生成的和指令无关的元素无能为力,比如:挂载在tab上只能删除标签,无法删除对应面板。

可以使用全局权限判断函数,使用v-if实现

<template><el-tab-pane v-if="checkPermission(['admin'])"></el-tab-pane>
</template>
<script>
export default{methods:{checkPermission(permissionRoles){return roles.some(role => {return permissionRoles.include(role);});}}
}
</script>

http://chatgpt.dhexx.cn/article/nB3S7uUH.shtml

相关文章

Vue 路由权限控制

当我们在做后台管理系统的时候&#xff0c;都会涉及到系统左侧的菜单树如何动态显示的问题。目前基本上都是RBAC的解决方案&#xff0c;即Role-Based Access Control&#xff0c;权限与角色相关联&#xff0c;用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了…

【Git CMD】Git常用命令总结

目录 0 git的工作区、暂存区、本地仓库和远程仓库0.1 图解0.2 解析 1 本地仓库1.1 创建版本库1.2 分支1.2.1 查看本地仓库的分支信息1.2.2 创建分支1.2.3 切换分支1.2.4 重命名分支1.2.5 合并分支1.2.6 删除分支 1.3 添加文件到暂存区1.3.1 添加单个文件1.3.2 添加多个文件1.3.…

Git常用命令大全(从入门到使用,学不会评论区骂我)

Git常用命令大全 1&#xff1a;Git全局设置 当安装Git后首先要做的事情是设置用户名称和email地址。这是非常重要的&#xff0c;因为每次Git提交都会使用该用户信息。在Git 命令行中执行下面命令&#xff1a; 设置用户信息 git config --global user.name “你的用户名” …

Git常用命令及方法大全

下面是我整理的常用 Git 命令清单。几个专用名词的译名如下。 Workspace&#xff1a;工作区Index / Stage&#xff1a;暂存区Repository&#xff1a;仓库区&#xff08;或本地仓库&#xff09;Remote&#xff1a;远程仓库 本地分支关联远程&#xff1a;git branch --set-upstre…

Git 常用命令大全

一、 Git 常用命令速查 git branch 查看本地所有分支git status 查看当前状态 git commit 提交 git branch -a 查看所有的分支git branch -r 查看远程所有分支git commit -am "init" 提交并且加注释 git remote add origin git192.168.1.119:ndshowgit push origin …

Git常用命令大全

Git常用命令大全 下面是我整理的常用 Git 命令清单。几个专用名词的译名如下。 Workspace&#xff1a;工作区Index / Stage&#xff1a;暂存区Repository&#xff1a;仓库区&#xff08;或本地仓库&#xff09;Remote&#xff1a;远程仓库 本地分支关联远程 git branch --set-u…

git常用命令总结

1 git概述 1.1 简介 git是分布式版本控制系统&#xff08;Distributed Version Control System&#xff0c;简称DVCS&#xff09;&#xff0c;分为两种仓库 &#xff1a;本地仓库和远程仓库。 本地仓库&#xff1a;是在开发人员自己电脑上的Git仓库远程仓库&#xff1a;是在…

20 个最常用的 Git 命令用法说明及示例

在这篇文章中&#xff0c;我将介绍在使用 Git 时最常使用的 20 个命令。 作者 | Sahiti Kappagantula 译者 | 弯月&#xff0c;责编 | 屠敏 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 以下为译文&#xff1a; 以下是这些Git命令&#xff1a; git config git…

Git基本命令大全

点击上方“小白学视觉”&#xff0c;选择加"星标"或“置顶” 重磅干货&#xff0c;第一时间送达 1、git clone -b <指定分支名> <远程仓库地址> 克隆指定分支 如&#xff1a; git clone -b bestore_master ssh://gitgit-ssh.xxx.com/xxx.git 2、 git bra…

常用git命令总结大全

目录 一、常用命令 1、git init 2、git add 文件名 3、git commit -m “备注” 4、git status 与 git diff 5、git show commit_id 查看某次修改 6、git log 与 git reflow 7、git pull (--rebase) 8、git push (-u) 与 git branch (-u) 9、git reset --hard 与 git…

Git常用命令

这是一篇笔记 //查看某个命令文档 git help <command> git <command> -h git <command> --help1.基本操作 用户配置 git config --global user.name "bettyaner" git config --global user.email bettyaner163.com配置级别 –local&#xff08…

Git 常用命令速查表(收藏大全)

目录 一、新建代码库 二、配置 三、增加/删除/修改文件 四、代码提交 五、分支 六、标签 七、查看信息 八、远程操作 九、撤销 十、其他 名词 master: 默认开发分支 origin: 默认远程版本库 Index / Stage&#xff1a;暂存区 Workspace&#xff1a;工作区 Reposito…

【深度学习】ResNet50

结构 ResNet50结构&#xff1a; 推荐查看&#xff1a;caffe可视化版 resnet50中1x1filter的作用&#xff1a; 1、在shortcut connection block的残差层中使用1x1的fiter先降维&#xff08;channel&#xff09;&#xff0c;然后再使用1x1的fiter升维,使残差层输出与恒等映射…

ResNet-50 结构

ResNet有2个基本的block&#xff0c;一个是Identity Block&#xff0c;输入和输出的dimension是一样的&#xff0c;所以可以串联多个&#xff1b;另外一个基本block是Conv Block&#xff0c;输入和输出的dimension是不一样的&#xff0c;所以不能连续串联&#xff0c;它的作用本…

ResNet 简介

ResNet 本文对resnet进行介绍&#xff0c;文章目录如下&#xff1a; ResNet 历史ResNet 亮点为何层数不能太深residual 残差模块介绍网络结构BN 层迁移学习 本文参考资料有&#xff1a; 6.1 ResNet网络结构&#xff0c;BN以及迁移学习详解 https://www.bilibili.com/video/…

Resnet

再上一偏博文中我们说到越复杂的问题需要越深层的神经网络拟合&#xff0c;但是越深层的神经网络越难训练&#xff0c;原因可能是过拟合以及损失函数的局部最优解过多&#xff08;鞍点过多&#xff1f;导致经过相同的epoch更深的网络的trainerror大于较浅的网络&#xff0c;因为…

ResNet网络详解

ResNet ResNet在2015年由微软实验室提出&#xff0c;斩获当年lmageNet竞赛中分类任务第一名&#xff0c;目标检测第一名。获得coco数据集中目标检测第一名&#xff0c;图像分割第一名。 ResNet亮点 1.超深的网络结构(突破1000层) 2.提出residual模块 3.使用Batch Normalizat…

1 通俗易懂解释Resnet50

通俗易懂Resnet50网络结构分析 1 Why(该网络要解决什么样的问题)1.1 什么叫梯度消失和梯度爆炸 2 How(如何解决该问题)2.1 直观解释2.2 残差是什么2.3 网络结构 3 what 结果怎么样 1 Why(该网络要解决什么样的问题) 理论上网络越来越深&#xff0c;获取的信息越多&#xff0c;…

ResNet50网络结构

代码&#xff1a; import keras keras.utils.plot_model(keras.applications.ResNet50(include_topTrue,input_shape(224,224,3),weightsNone), to_fileimage_model.png, show_shapesTrue) ResNet50的标准输入为224x224&#xff0c;avg_pool&#xff08;-3层&#xff09;及之…