【Vue面试题二十八】、vue要做权限管理该怎么做?如果控制到按钮级别的权限怎么做?
文章底部有个人公众号:热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享? 踩过的坑没必要让别人在再踩,自己复盘也能加深记忆。利己利人、所谓双赢。

面试官:vue要做权限管理该怎么做?如果控制到按钮级别的权限怎么做?

一、是什么
权限是对特定资源的访问许可,所谓权限控制,也就是确保用户只能访问到被分配的资源
而前端权限归根结底是请求的发起权,请求的发起可能有下面两种形式触发
- 页面加载触发
- 页面上的按钮点击触发
总的来说,所有的请求发起都触发自前端路由或视图
所以我们可以从这两方面入手,对触发权限的源头进行控制,最终要实现的目标是:
-
路由方面,用户登录后只能看到自己有权访问的导航菜单,也只能访问自己有权访问的路由地址,否则将跳转
4xx提示页 -
视图方面,用户只能看到自己有权浏览的内容和有权操作的控件
-
最后再加上请求控制作为最后一道防线,路由可能配置失误,按钮可能忘了加权限,这种时候请求控制可以用来兜底,越权请求将在前端被拦截
二、如何做
前端权限控制可以分为四个方面:
- 接口权限
- 按钮权限
- 菜单权限
- 路由权限
接口权限
接口权限目前一般采用jwt的形式来验证,没有通过的话一般返回401,跳转到登录页面重新进行登录
登录完拿到token,将token存起来,通过axios请求拦截器进行拦截,每次请求的时候头部携带token
axios.interceptors.request.use(config => {config.headers['token'] = cookie.get('token')return config
})
axios.interceptors.response.use(res=>{},{response}=>{if (response.data.code === 40099 || response.data.code === 40098) { //token过期或者错误router.push('/login')}
})
路由权限控制
方案一
初始化即挂载全部路由,并且在路由上标记相应的权限信息,每次路由跳转前做校验
const routerMap = [{path: '/permission',component: Layout,redirect: '/permission/index',alwaysShow: true, // will always show the root menumeta: {title: 'permission',icon: 'lock',roles: ['admin', 'editor'] // you can set roles in root nav},children: [{path: 'page',component: () => import('@/views/permission/page'),name: 'pagePermission',meta: {title: 'pagePermission',roles: ['admin'] // or you can only set roles in sub nav}}, {path: 'directive',component: () => import('@/views/permission/directive'),name: 'directivePermission',meta: {title: 'directivePermission'// if do not set roles, means: this page does not require permission}}]}]
这种方式存在以下四种缺点:
-
加载所有的路由,如果路由很多,而用户并不是所有的路由都有权限访问,对性能会有影响。
-
全局路由守卫里,每次路由跳转都要做权限判断。
-
菜单信息写死在前端,要改个显示文字或权限信息,需要重新编译
-
菜单跟路由耦合在一起,定义路由的时候还有添加菜单显示标题,图标之类的信息,而且路由不一定作为菜单显示,还要多加字段进行标识
方案二
初始化的时候先挂载不需要权限控制的路由,比如登录页,404等错误页。如果用户通过URL进行强制访问,则会直接进入404,相当于从源头上做了控制
登录后,获取用户的权限信息,然后筛选有权限访问的路由,在全局路由守卫里进行调用addRoutes添加路由
import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css'// progress bar style
import { getToken } from '@/utils/auth' // getToken from cookieNProgress.configure({ showSpinner: false })// NProgress Configuration// permission judge function
function hasPermission(roles, permissionRoles) {if (roles.indexOf('admin') >= 0) return true // admin permission passed directlyif (!permissionRoles) return truereturn roles.some(role => permissionRoles.indexOf(role) >= 0)
}const whiteList = ['/login', '/authredirect']// no redirect whitelistrouter.beforeEach((to, from, next) => {NProgress.start() // start progress barif (getToken()) { // determine if there has token/* has token*/if (to.path === '/login') {next({ path: '/' })NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it} else {if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息store.dispatch('GetUserInfo').then(res => { // 拉取user_infoconst roles = res.data.roles // note: roles must be a array! such as: ['editor','develop']store.dispatch('GenerateRoutes', { roles }).then(() => { // 根据roles权限生成可访问的路由表router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record})}).catch((err) => {store.dispatch('FedLogOut').then(() => {Message.error(err || 'Verification failed, please login again')next({ path: '/' })})})} else {// 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓if (hasPermission(store.getters.roles, to.meta.roles)) {next()//} else {next({ path: '/401', replace: true, query: { noGoBack: true }})}// 可删 ↑}}} else {/* has no token*/if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入next()} else {next('/login') // 否则全部重定向到登录页NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it}}
})router.afterEach(() => {NProgress.done() // finish progress bar
})
按需挂载,路由就需要知道用户的路由权限,也就是在用户登录进来的时候就要知道当前用户拥有哪些路由权限
这种方式也存在了以下的缺点:
- 全局路由守卫里,每次路由跳转都要做判断
- 菜单信息写死在前端,要改个显示文字或权限信息,需要重新编译
- 菜单跟路由耦合在一起,定义路由的时候还有添加菜单显示标题,图标之类的信息,而且路由不一定作为菜单显示,还要多加字段进行标识
菜单权限
菜单权限可以理解成将页面与理由进行解耦
方案一
菜单与路由分离,菜单由后端返回
前端定义路由信息
{name: "login",path: "/login",component: () => import("@/pages/Login.vue")
}
name字段都不为空,需要根据此字段与后端返回菜单做关联,后端返回的菜单信息中必须要有name对应的字段,并且做唯一性校验
全局路由守卫里做判断
name字段都不为空,需要根据此字段与后端返回菜单做关联,后端返回的菜单信息中必须要有name对应的字段,并且做唯一性校验
全局路由守卫里做判断
function hasPermission(router, accessMenu) {if (whiteList.indexOf(router.path) !== -1) {return true;}let menu = Util.getMenuByName(router.name, accessMenu);if (menu.name) {return true;}return false;}Router.beforeEach(async (to, from, next) => {if (getToken()) {let userInfo = store.state.user.userInfo;if (!userInfo.name) {try {await store.dispatch("GetUserInfo")await store.dispatch('updateAccessMenu')if (to.path === '/login') {next({ name: 'home_index' })} else {//Util.toDefaultPage([...routers], to.name, router, next);next({ ...to, replace: true })//菜单权限更新完成,重新进一次当前路由}} catch (e) {if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入next()} else {next('/login')}}} else {if (to.path === '/login') {next({ name: 'home_index' })} else {if (hasPermission(to, store.getters.accessMenu)) {Util.toDefaultPage(store.getters.accessMenu,to, routes, next);} else {next({ path: '/403',replace:true })}}}} else {if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入next()} else {next('/login')}}let menu = Util.getMenuByName(to.name, store.getters.accessMenu);Util.title(menu.title);
});Router.afterEach((to) => {window.scrollTo(0, 0);
});
每次路由跳转的时候都要判断权限,这里的判断也很简单,因为菜单的name与路由的name是一一对应的,而后端返回的菜单就已经是经过权限过滤的
如果根据路由name找不到对应的菜单,就表示用户有没权限访问
如果路由很多,可以在应用初始化的时候,只挂载不需要权限控制的路由。取得后端返回的菜单后,根据菜单与路由的对应关系,筛选出可访问的路由,通过addRoutes动态挂载
这种方式的缺点:
- 菜单需要与路由做一一对应,前端添加了新功能,需要通过菜单管理功能添加新的菜单,如果菜单配置的不对会导致应用不能正常使用
- 全局路由守卫里,每次路由跳转都要做判断
方案二
菜单和路由都由后端返回
前端统一定义路由组件
const Home = () => import("../pages/Home.vue");
const UserInfo = () => import("../pages/UserInfo.vue");
export default {home: Home,userInfo: UserInfo
};
后端路由组件返回以下格式
[{name: "home",path: "/",component: "home"},{name: "home",path: "/userinfo",component: "userInfo"}
]
在将后端返回路由通过addRoutes动态挂载之间,需要将数据处理一下,将component字段换为真正的组件
如果有嵌套路由,后端功能设计的时候,要注意添加相应的字段,前端拿到数据也要做相应的处理
这种方法也会存在缺点:
- 全局路由守卫里,每次路由跳转都要做判断
- 前后端的配合要求更高
按钮权限
方案一
按钮权限也可以用v-if判断
但是如果页面过多,每个页面页面都要获取用户权限role和路由表里的meta.btnPermissions,然后再做判断
这种方式就不展开举例了
方案二
通过自定义指令进行按钮权限的判断
首先配置路由
{path: '/permission',component: Layout,name: '权限测试',meta: {btnPermissions: ['admin', 'supper', 'normal']},//页面需要的权限children: [{path: 'supper',component: _import('system/supper'),name: '权限测试页',meta: {btnPermissions: ['admin', 'supper']} //页面需要的权限},{path: 'normal',component: _import('system/normal'),name: '权限测试页',meta: {btnPermissions: ['admin']} //页面需要的权限}]
}
自定义权限鉴定指令
import Vue from 'vue'
/**权限指令**/
const has = Vue.directive('has', {bind: function (el, binding, vnode) {// 获取页面按钮权限let btnPermissionsArr = [];if(binding.value){// 如果指令传值,获取指令参数,根据指令参数和当前登录人按钮权限做比较。btnPermissionsArr = Array.of(binding.value);}else{// 否则获取路由中的参数,根据路由的btnPermissionsArr和当前登录人按钮权限做比较。btnPermissionsArr = vnode.context.$route.meta.btnPermissions;}if (!Vue.prototype.$_has(btnPermissionsArr)) {el.parentNode.removeChild(el);}}
});
// 权限检查方法
Vue.prototype.$_has = function (value) {let isExist = false;// 获取用户按钮权限let btnPermissionsStr = sessionStorage.getItem("btnPermissions");if (btnPermissionsStr == undefined || btnPermissionsStr == null) {return false;}if (value.indexOf(btnPermissionsStr) > -1) {isExist = true;}return isExist;
};
export {has}
在使用的按钮中只需要引用v-has指令
<el-button @click='editClick' type="primary" v-has>编辑</el-button>
小结
关于权限如何选择哪种合适的方案,可以根据自己项目的方案项目,如考虑路由与菜单是否分离
权限需要前后端结合,前端尽可能的去控制,更多的需要后台判断
相关文章:
【Vue面试题二十八】、vue要做权限管理该怎么做?如果控制到按钮级别的权限怎么做?
文章底部有个人公众号:热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享? 踩过的坑没必要让别人在再踩,自己复盘也能加深记忆。利己利人、所谓双赢。 面试官:vue要做权限管理该怎么做…...
1、验证1101序列(Moore)
题目要求: 用Moore型状态机验证1101序列。 题目描述: 用使用状态机验证1101序列,注意:允许重复子序列。如图 端口描述: module moore_1101(input clk,//时钟信号input clr,//reset复位信号,高电平有效in…...
Java基础面试-BeanFactroy和ApplicationContext
ApplicationContext是BeanFactory的子接口 ApplicationContext提供了更完整的功能: 继承MessageSource,因此支持国际化。统一的资源文件访问方式。提供在监听器中注册bean的事件。同时加载多个配置文件。载入多个(有继承关系)上下文,使得每一个上下文都…...
js的入口函数
JavaScript的入口函数是指在HTML页面加载完毕后,JavaScript代码开始执行的函数。这个函数一般被称为onload函数,它的作用是在页面加载完成后执行一些初始化操作,或者对页面进行动态修改。 在HTML页面中,可以通过以下方式设置入口…...
[cpp primer随笔] 10. 函数重载与重载决议
本篇将介绍C函数重载的概念,及重载决议规则。 一、函数重载 同一作用域内,函数名相同,而形参列表不同的函数称之为重载函数(overloaded function)。 需要特别注意:以下情况看似形参列表不同,实则等价,无法…...
java_Stream API
文章目录 一、Stream API vs 集合二、Stream 使用的执行流程2.1、创建Stream2.1、中间操作2.1.1. filter2.1.2. limit2.1.3. skip2.1.4. distinct2.1.5. map2.1.6. sorted 一、Stream API vs 集合 Stream API 关注的是多个数据的计算(排序、查找、过滤、映射、遍历…...
红队专题-工具Fscan
红队专题 招募六边形战士队员简介主要功能 ubuntu 安装windows 安装常用命令:项目框架源文件common目录Plugins目录Webscan目录爆破插件common.Scantype Webtitle函数webpoc扫描类型指纹识别 免杀源码特征 参考链接 招募六边形战士队员 一起学习 代码审计、安全开发…...
【宏实现二进制奇偶位交换】
文章目录 一. 二进制奇偶位交换说明意思?二. 解题思路三. 代码验证四. 总结 一. 二进制奇偶位交换说明意思? 就是一个int类型的整数在操作系统下是32位二进制01序列,第一位和第二位交换,第二位和第三位交换,依次类推。…...
【Java零基础入门到就业】第一天:java简介和cmd窗口的一些常见命令
1、java简介 Java是一种基于类的、面向对象的编程语言,它被设计成具有尽可能少的实现依赖。它旨在让应用程序开发人员编写一次,并在任何地方运行(WORA),这意味着编译后的Java代码可以在所有支持Java的平台上运行,而无需重新编译。…...
ubuntu下yolov5 tensorrt模型部署
文章目录 ubuntu下yolov5 tensorrt模型部署一、Ubuntu18.04环境配置1.1 安装工具链和opencv1.2 安装Nvidia相关库1.2.1 安装Nvidia显卡驱动1.2.2 安装 cuda11.31.2.3 安装 cudnn8.21.2.4 下载 tensorrt8.4.2.41.2.5 下载仓库TensorRT-Alpha并设置 二、从yolov5源码中导出onnx文…...
windows Vscode 连接 虚拟机,超详细,含免密免ip配置 以 linux 虚拟机为例
我们这里使用 ssh 进行连接,不了解 ssh 的也没关系,感兴趣的可以自己了解一下。 我的虚拟机是 Ubuntu20.04,如果出现与 Centos 不一样的操作可以自行替换。 (应该不会有??) 一 . 登录虚拟机~&a…...
【Unity】VR基础开发2项目准备-VR基本场景构建(OpenXR方向)
【文字内容】 稍等一会儿,编辑器界面就出现了。有可能你的界面布局会有所不同,可以在右上角点击布局,选择Tall,布局切换后在来到左边窗口的左上角,将Game选项卡拖放到下方Dock,这样就可以得到和我一样的布局了。 简单介绍一下。 场景面板是对游戏进行可视化编辑的地方…...
git checkout 命令
一、拉取更新某个分支中某个文件/文件夹 git checkout 分支名 文件路径 git checkout temporary src/components/tools/UserMenu.vue 二、回退某个文件到前一个提交 git checkout HEAD^ 文件路径 git checkout HEAD^ src/components/tools/UserMenu.vue 如果你要回退到…...
二.镜头知识之镜头总长,法兰距,安装接口
二.镜头知识之镜头总长,法兰距,安装接口 文章目录 二.镜头知识之镜头总长,法兰距,安装接口2.1 线激光模组镜头的FBL 与 TTL(Total Track Length) 镜头总长2.2 相机法兰距2.3 线激光模组镜头的TTL 以及 From Barrel bottom to imag…...
Android studio控制台 输出乱码解决方法
在AS的安装目录,找到 studio64.exe.vmoptions 文件, 用编辑器打开文件,在最后面加上下面的代码: -Defile.encodingUTF-8然后 重启AS。 注意: 下面两种方式也能打开studio64.exe.vmoptions 文件,但是需要确…...
Hermes - 指尖上的智慧:自定义问答系统的崭新世界
在希腊神话中,有一位智慧与消息的传递者神祇,他就是赫尔墨斯(Hermes)。赫尔墨斯是奥林匹斯众神中的一员,传说他是乌尔阿努斯(Uranus)和莫伊拉(Maia)的儿子,同…...
机器学习笔记 - 使用3D卷积神经网络进行视频分类
1、导入相应的库 3D CNN 使用三维滤波器来执行卷积。内核能够在三个方向上滑动,而在 2D CNN 中它可以在二维上滑动。 首先安装并导入必要的库,用于处理ZIP文件内容的Remotezip 、用于使用进度条的tqdm 、用于处理视频文件的OpenCV 、用于执行更复杂的张量操作的einop…...
在Unity中挂载C#脚本的三种方法
第一种 ①在Project(工程)窗口的某个文件夹中(也可以选择新建在Assets(资源根目录)中),然后单击鼠标右键,选择Create->C# Script 注意:扩展名在Unity编辑器中是隐藏…...
vue elementui的select组件实现滑到底部分页请求后端接口
vue elementui的select组件实现滑到底部分页请求后端接口 1.实现效果2.实现原理 1.实现效果 老规矩,直接上最后的实现效果 2.实现原理 直接上代码 <el-form-item class"diagmosisItem" label"诊断" v-scroll"handleScroll">…...
【Java 进阶篇】JavaScript电灯开关案例:从原理到实现
JavaScript是一门强大的编程语言,它可以用来创建各种交互式网页应用。在这篇博客中,我们将通过一个简单的电灯开关案例来深入了解JavaScript的基础概念,包括HTML、CSS和JavaScript的结合使用。我们将从头开始构建这个案例,逐步引入…...
IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...
DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
