vue权限控制和动态路由
思路
- 登录:当用户填写完账号和密码后向服务端验证是否正确,验证通过之后,服务端会返回一个
token,拿到token之后(我会将这个token存贮到localStore中,保证刷新页面后能记住用户登录状态),前端会根据token再去拉取一个user_info的接口来获取用户的详细信息(如用户权限,用户名等等信息)。 - 权限验证:通过
token获取用户对应的role,动态根据用户的role算出其对应有权限的路由,通过router.addRoutes动态挂载这些路由。
路由定义
路由分为两种:constantRoutes 和 asyncRoutes
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>相关文章:
vue权限控制和动态路由
思路 登录:当用户填写完账号和密码后向服务端验证是否正确,验证通过之后,服务端会返回一个token,拿到token之后(我会将这个token存贮到localStore中,保证刷新页面后能记住用户登录状态)…...
利用正则表达式删掉代码中的所有注释-pycharm为例
首先删除注释 打开您想要删除注释的Python文件。 使用快捷键 Ctrl Shift R 打开 "Replace in Files"(在文件中替换)对话框。 在 "Find"(查找)框中输入以下正则表达式,以查找所有行中的注释内容…...
【java基础】内部类、局部内部类、匿名内部类、静态内部类
内部类 内部类就是定义在另一个类中的类。我们使用内部类的原因主要有以下两点 内部类可以对同一个包中的其他类隐藏内部类方法可以访问定义这个类的作用域中的数据,包括原本私有的数据 public class A {class B {} }我们使用内部类可以访问外部类的所有属性&…...
react renderProps学习记录
react renderProps学习记录1.引入2.改一下呢3.再改一下呢4.总结一下如何向组件内部动态传入带内容的结构(标签)?children propsrender props1.引入 上代码: import React, { Component } from react import ./index.css export default class Parent extends Com…...
关于tf.gather函数batch_dims参数用法的理解
关于tf.gather函数batch_dims参数用法的理解0 前言1. 不考虑batch_dims2. 批处理(考虑batch_dims)2.1 batch_dims12.2 batch_dims02.3 batch_dims>22.4 batch_dims再降为12.5 再将axis降为12.6 batch_dims<02.7 batch_dims总结3. 补充4. 参数和返回值5. 其他相关论述6. 附…...
日常操作linux常用命令
cd /mnt/opt/cqstt/logs/stt-erp docker logs -f --tail1000 stt-erp # 查看物理CPU个数 cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l # 查看每个物理CPU中core的个数(即核数) cat /proc/cpuinfo| grep "cpu cores"| uniq # 查看逻辑CPU的…...
【Java集合框架】篇二:Collection接口方法
JDK不提供此接口的任何直接实现类,而是提供更具体的子接口(如:Set和List)去实现。 Collection 接口是 List和Set接口的父接口,该接口里定义的方法既可用于操作 Set 集合,也可用于操作 List 集合。方法如下…...
PHP入门指南:简单易学的语法和丰富的调试工具与安全性最佳实践
PHP是一种非常流行的服务器端编程语言,它被广泛地应用于Web开发中。如果您想学习Web开发,那么PHP是一个非常好的选择。在本文中,我将介绍PHP的一些基础知识,包括语法、变量、函数、数组、数据库连接、调试和安全性等。PHP的语法PH…...
前端面试题--HTML篇
一、src和href的区别src指向外部资源的位置,指向的内容会嵌入到文档中当前标签所在的位置;href指向网络资源的位置,建立和当前元素或当前文档之间的链接。二、对HTML语义化的理解根据内容的结构化,选择合适的标签。优点࿱…...
SpringBoot集成ElasticSearch,实现模糊查询,批量CRUD,排序,分页,高亮
导入elasticsearch依赖在pom.xml里加入如下依赖:<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency>非常重要:检查依赖版本…...
常用Swagger注解汇总
常用Swagger注解汇总 前言 在实际编写后端代码的过程中,我们可能经常使用到 swagger 注解,但是会用不代表了解,你知道每个注解都有什么属性吗?你都用过这些属性吗?了解它们的作用吗?本文在此带大家总结一下…...
关于 TypeScript 声明文件
declare var 声明全局变量declare function 声明全局方法declare class 声明全局类declare enum 声明全局枚举类型declare namespace 声明(含有子属性的)全局对象interface 和 type 声明全局类型export 导出变量export namespace 导出(含有子…...
SpringBoot学习-原理篇
SpringBoot原理篇springboot技术本身就是为了加速spring程序的开发的,可以大胆的说,springboot技术没有自己的原理层面的设计,仅仅是实现方案进行了改进。将springboot定位成工具,你就不会去想方设法的学习其原理了。就像是将木头…...
目标检测YOLOv5数据集怎么找?
完整的配置-标注-训练-识别在我这篇博客小白YOLOv5全流程-训练实现数字识别_yolov5数字识别_牛大了2022的博客-CSDN博客 模型部分剖析可以看我每周深度学习笔记部分。关于训练的数据集怎么搞很多人问过我,我在这篇文章给大家一点我的经验和建议。 数据集是什么 简…...
安卓短信自动填充踩坑
安卓短信自动填充踩坑 前言 最近弄了个短信自动填充功能,一开始觉得很简单,不就是动态注册个广播接收器去监听短信消息不就可以了吗?结果没这么简单,问题就出在机型的适配上。小米的短信权限、荣耀的短信监听都是坑,…...
【抽象类和接口的区别】
抽象类和接口都是Java中实现多态的机制,它们都是用来约束子类必须要实现的方法。但是它们有以下区别: 实现方式 实现方式:抽象类是一个类,而接口是一个接口。一个类只能继承一个抽象类,但可以实现多个接口。 构造方…...
接口导出文件功能
1.写接口 export function getExport(params) { return fetch({ url: ******.export, method: post, data: params, responseType:blob, }) } 2.编写前端页面 <el-button :loading"exportDisable" :disabled&quo…...
深圳大学计软《面向对象的程序设计》实验9 期中复习
A. 机器人变身(类与对象)【期中模拟】 题目描述 编写一个机器人类,包含属性有机器名、血量、伤害值、防御值、类型和等级。其中血量、伤害和防御和等级、类型相关: 普通型机器人,类型为N,血量、伤害、防…...
python之异步编程
一、异步编程概述 异步编程是一种并发编程的模式,其关注点是通过调度不同任务之间的执行和等待时间,通过减少处理器的闲置时间来达到减少整个程序的执行时间;异步编程跟同步编程模型最大的不同就是其任务的切换,当遇到一个需要等…...
为什么很多计算机专业大学生毕业后还会参加培训?
基于IT互联网行业越来越卷的现状,就算是科班出身,很多也是达不到用人单位的要求。面对这样的现实情况,有的同学会选择继续深造,比如考个研,去年考研人数457万人次,可见越来越的同学是倾向考研提升学历来达到…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...
算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...
