前端面试常考之——Vue前端路由权限控制(vuex版本)

文章目录

      • 前端权限控制思路
        • 1. 菜单的权限控制
      • Vue的权限控制实现
        • 1. 菜单的控制
        • 2. 界面的控制
        • 3. 按钮的控制
        • 4. 请求和响应的控制
          • 请求控制
          • 响应控制
      • 小结

前端权限控制思路

1. 菜单的权限控制
  • 菜单的控制
    在登录请求中,会得到权限数据,当然,这个需要后端返回数据的支持。前端根据权限数据展示对应的菜单,点击菜单才能查看相关的界面
  • 界面的控制
    如果用户没有登录,手动在地址栏敲入管理界面的地址,则需要跳转到登录页。如用户已经登录,可是手动敲入非权限内的地址,则需要跳转404界面
  • 按钮的控制
    在某个菜单的界面,还得根据权限数据,展示出可进行操作的按钮,比如删除,修改增加
  • 请求和响应的控制
    如果用户通过非常规的操作,比如通过浏览器调试工具将某些禁用的按钮变成启用状态,此时发的请求,也应当被前端所拦截

Vue的权限控制实现

1. 菜单的控制
  • 查看登录之后获取到的数据

    {"data": {"id": 500,"rid": 0,"username": "admin","mobile": "13999999999","email": "123999@qq.com","token": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjUwMCwicmlkIjowLCJpYXQiOjE1MTI1NDQyOTksImV4cCI6MTUxMjYzMDY5OX0.eGrsrvwHm-tPsO9r_pxHIQ5i5L1kX9RX444uwnRGaIM"},"rights": [{"id": 125,"authName": "用户管理","icon": "icon-user","children": [{"id": 110,"authName": "用户列表","path": "users","rights": ["view","edit","add","delete"]}]},{"id": 103,"authName": "角色管理","icon": "icon-tijikongjian","children": [{"id": 111,"authName": "角色列表","path": "roles","rights": ["view","edit","add","delete"]}]},{"id": 101,"authName": "商品管理","icon": "icon-shangpin","children": [{"id": 104,"authName": "商品列表","path": "goods","rights": ["view","edit","add","delete"]},{"id": 121,"authName": "商品分类","path": "categories","rights": ["view","edit","add","delete"]}]}],"meta": {"msg": "登录成功","status": 200}
    }
    

    在这部分数据中,除了该用户的基本信息之外,还有两个字段很关键

    • token,由于前端用户的状态保持
    • rights:该用户具备的权限数据,一级权限就对应一级菜单,二级权限就对应二级菜单
  • 根据rights中的数据,动态渲染左侧菜单栏,数据在Login.vue得到,但是在Home.vue才使用,所以可以把数据用vuex进行维护

    • vuex——index.js

      import Vue from 'vue'
      import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {rightList: JSON.parse(sessionStorage.getItem('rightList') || '[]'),username: sessionStorage.getItem('username')},mutations: {setRightList(state, data) {state.rightList = datasessionStorage.setItem('rightList', JSON.stringify(data))},setUsername(state, data) {state.username = datasessionStorage.setItem('username', data)}},actions: {},getters: {}
      })
      
    • Login.vue

      login() {this.$refs.loginFormRef.validate(async (valid) => {if (!valid) returnconst { data: res } = await this.$http.post('login', this.loginForm)if (res.meta.status !== 200) return this.$message.error('登录失败!')console.log(res)this.$store.commit('setRightList', res.rights)this.$store.commit('setUsername', res.data.username)this.$message.success('登录成功')this.$router.push('/home')})},
      
    • Home.vue

      import { mapState } from 'vuex'
      computed: {...mapState(['rightList', 'username'])},
      created() {this.activePath = window.sessionStorage.getItem('activePath')// 初始化menulist菜单栏的数据this.menulist = this.rightList},
      
  • 刷新界面菜单消失

    • 原因分析

      因为菜单数据是登录之后才获取的,存放在vuex中
      一旦刷新界面,vuex中的数据会初始化为空
      因此,需要将权限数据存储在sessionStorage中,并让其和vuex中的数据保持同步
      
    • 代码解决

      export default new Vuex.Store({state: {rightList: JSON.parse(sessionStorage.getItem('rightList') || '[]'),username: sessionStorage.getItem('username')},mutations: {setRightList(state, data) {state.rightList = datasessionStorage.setItem('rightList', JSON.stringify(data))},setUsername(state, data) {state.username = datasessionStorage.setItem('username', data)}},actions: {},getters: {}
      })
      
    • 退出按钮的逻辑

      logout() {// 删除sessionStorage中的数据sessionStorage.clear()this.$router.push('/login')// 删除vuex中的数据,让当前的界面刷新即可window.location.reload()},
      
2. 界面的控制
  1. 正常的逻辑是通过登录界面,登录成功之后跳转到管理平台界面,但是如果用户直接敲管理平台的地址,也是可以跳过登录的步骤,所以应该在某个时机判断用户是否登录
  • 如何判断是否登录

    login() {// 登录时存储tokensessionStorage.setItem('token', res.data.token)},
    
  • 什么时机

    • 路由导航守卫

      router.beforeEach((to, from, next) => {if (to.path === '/login') {next()} else {const token = sessionStorage.getItem('token')if (!token) {next('/login')} else {next()}}
      })
      
  1. 虽然菜单项已经被控制住了,但是路由信息还是完整的存在于浏览器,正比如zhangshan这个用户并不具备角色这个菜单,但是在地址栏中敲入/roles的地址,依然可以访问角色界面。
  • 路由导航守卫
    路由导航守卫固然可以在每次路由地址发生变化的时候,从vuex中取出rightList判断用户将要访问的界面有没有权限。不过从另一个角度来说,这个用户不具备权限的路由,是否也应该压根就不存在呢?

  • 动态路由

    • 登录成功之后动态添加
    // router.js
    import Vue from 'vue'
    import Router from 'vue-router'
    import Login from '@/components/Login.vue'
    import Home from '@/components/Home.vue'
    import Welcome from '@/components/Welcome.vue'
    import Users from '@/components/user/Users.vue'
    import Roles from '@/components/role/Roles.vue'
    import GoodsCate from '@/components/goods/GoodsCate.vue'
    import GoodsList from '@/components/goods/GoodsList.vue'
    import NotFound from '@/components/NotFound.vue'
    import store from '@/store'Vue.use(Router)// 动态路由规则映射
    const userRule = { path: '/users', component: Users }
    const roleRule = { path: '/roles', component: Roles }
    const goodRule = { path: '/goods', component: GoodsList }
    const categoryRule = { path: '/categories', component: GoodsCate }const ruleMapping = {'users': userRule,'roles': roleRule,'goods': goodRule,'categories': categoryRule
    }const router = new Router({routes: [{path: '/',redirect: '/welcome'},{path: '/login',component: Login},{path: '/home',component: Home,redirect: '/welcome',children: [{ path: '/welcome', component: Welcome },// { path: '/users', component: Users },// { path: '/roles', component: Roles },// { path: '/goods', component: GoodsList },// { path: '/categories', component: GoodsCate }]},{path: '*',component: NotFound}]
    })
    // 动态路由
    export function initDynamicRoutes() {// 根据二级权限,对路由规则进行动态的添加console.log(router)const currentRoutes = router.options.routesconst rightList = store.state.rightListrightList.forEach(item => {item.children.forEach(item => {// item 二级权限const temp = ruleMapping[item.path]currentRoutes[2].children.push(temp)})})currentRoutes.forEach(item => {router.addRoute(item)})
    }
    // 路由导航守卫 拦截没登录时的权限路由
    router.beforeEach((to, from, next) => {if (to.path === '/login') {next()} else {const token = sessionStorage.getItem('token')if (!token) {next('/login')} else {next()}}
    })export default router
    
    // Login.vue
    import { initDynamicRoutes } from "@/router.js"
    login() {this.$refs.loginFormRef.validate(async (valid) => {if (!valid) returnconst { data: res } = await this.$http.post('login', this.loginForm)if (res.meta.status !== 200) return this.$message.error('登录失败!')console.log(res)this.$store.commit('setRightList', res.rights)this.$store.commit('setUsername', res.data.username)sessionStorage.setItem('token', res.data.token)this.$message.success('登录成功')// 根据用户所具备的权限,动态添加路由规则initDynamicRoutes()this.$router.push('/home')})},
    
    • App.vue中添加,防止登录后再次刷新后重新路由规则重新加载,菜单被初始化
    export default {name: 'app',created() {initDynamicRoutes() //动态添加路由规则}
    }
    
3. 按钮的控制

​ 虽然用户可以看到某些界面了,但是这个界面的一些按钮,该用户可能是没有权限的,因此,我们需要对组件中的一些按钮进行控制。用户不具权限的按钮就隐藏或者禁用,而在这块中,可以把该逻辑放到自定义指令中

  • permission.js 注册自定义指令

    import Vue from 'vue'
    import router from '@/router.js'
    Vue.directive('permission', {inserted(el, binding) {console.log(binding)const action = binding.value.actionconst effect = binding.value.effect// 判断 当前路由所对应的组件中,如何判断用户是否具备action的权限console.log(router.currentRoute.meta)if (router.currentRoute.meta.indexOf(action) == -1) {if (effect === 'disabled') {el.disabled = trueel.classList.add('is-disabled')} else {el.parentNode.removeChild(el)}}}
    })
    
  • main.js

    import './utils/permission.js' //引入到入口文件permission才会被加载
    
  • router.js 把路由元信息添加进来

    export function initDynamicRoutes() {// 根据二级权限,对路由规则进行动态的添加console.log(router)const currentRoutes = router.options.routesconst rightList = store.state.rightListrightList.forEach(item => {item.children.forEach(item => {// item 二级权限const temp = ruleMapping[item.path]// 把路由元信息添加进来temp.meta = item.rightscurrentRoutes[2].children.push(temp)})})currentRoutes.forEach(item => {router.addRoute(item)})
    }
    
  • 使用自定义指令

    v-permission="{action:'add'}"
    v-permission="{action:'edit', effect:'disabled'}"
    
4. 请求和响应的控制
请求控制
  • 除了登录请求都要带上token,这样服务器才可以鉴别你的身份

    // http.js
    import axios from 'axios'
    import Vue from 'vue'
    // 配置请求的跟路径, 目前用mock模拟数据, 所以暂时把这一项注释起来
    // axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/'axios.interceptors.request.use(req => {// console.log(req.url, req.method)if (req.url !== 'login') {// 不是登录的请求,我们应该在请求头中加入token数据req.headers.Authorization = sessionStorage.getItem('token')}return req
    })
    Vue.prototype.$http = axios
    
  • 如果发出了非权限内的请求,应该直接在前端范围内组织,虽然这个请求发送到服务器也会被拒绝

    // http.js
    import axios from 'axios'
    import Vue from 'vue'
    import router from '@/router.js'
    // 配置请求的跟路径, 目前用mock模拟数据, 所以暂时把这一项注释起来
    // axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/'// 请求方式和权限的映射
    const actionMapping = {'get': 'view','post': 'add','put': 'edit','delete': 'delete'
    }
    axios.interceptors.request.use(req => {// console.log(req.url, req.method)if (req.url !== 'login') {// 不是登录的请求,我们应该在请求头中加入token数据req.headers.Authorization = sessionStorage.getItem('token')// 判断非权限范围内的请求// router.currentRoute.meta// resful风格请求/*get请求     viewpost请求    addput请求     editdelete请求  delete[add view edit delete]*/const action = actionMapping[req.method]// 判断 action 是否存在当前路由的权限中const rights = router.currentRoute.metaif (rights && rights.indexOf(action) === -1) {// 没有权限alert('没有权限')return Promise.reject(new Error('没有权限'))}}return req
    })
    Vue.prototype.$http = axios
    
响应控制
  • 得到了服务器返回的状态码401,代表token超时或者被篡改了,此时应该强制跳转登录页

    axios.interceptors.response.use((res) => {if (res.data.meta.status === 401) {router.push('/login')sessionStorage.clear()// 通过Vuex的actions或mutations来清空或重置存储在store中的登录相关状态。this.$store.dispatch('logout')}return res
    })
    

小结

前端权限的实现必须要后端提供数据支持,否则无法实现.

返回的权限数据的结构,前后端需要沟通协商,怎样的数据使用起来才最方便.

4.1.菜单控制

  • 权限的数据需要在多组件之间共享,因此采用vuex.

  • 防止刷新界面,权限数据丢失,所以需要存储在sessionStorage,并且要保证两者的同步

4.2.界面控制

  • 路由的导航守卫可以防止跳过登录界面

  • 动态路由可以让不具备权限的界面的路由规则压根就不存在

4.3.按钮控制

  • 路由规则中可以增加路由元数据meta

  • 通过路由对象可以得到当前的路由规则,以及存储在此规则中的meta数据.

  • 自定义指令可以很方便的实现按钮控制

4.4.请求和响应控制

  • 请求拦截器和响应拦截器的使用.

  • 请求方式的约定restful

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/700328.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python中停止线程的方法

文章目录 1 threading.Event()方法2 子线程抛出异常,立刻停止 1 threading.Event()方法 一种常见的方法是使用标志位来通知线程应该停止。线程可以定期检查这个标志位,如果它被设置为停止,那么线程就结束其执行。下面是一个简单的例子&#…

NXP实战笔记(八):S32K3xx基于RTD-SDK在S32DS上配置LCU实现ABZ解码

目录 1、概述 2、SDK配置 2.1、IO配置 2.2、TRGMUX配置 2.3、LCU配置 2.4、Trgmux配置 2.5、Emios配置 2.6、代码实现 1、概述 碰到光电编码器、磁编码器等,有时候传出来的位置信息为ABZ的方式,在S32K3里面通过TRGMUX、LCU、Emios结合的方式可以实现ABZ解码。 官方…

HCIA(11)OSPF 数据包构成(Hello、DBD、LSR、LSU、LSAck包)、状态机、工作流程(建立邻居关系、主从关系协商、LSDB同步)

OSPF(Open Shortest Path First)是IETF组织开发的一个基于链路状态的内部网关协议(Interior Gateway Protocol)。 目前针对IPv4协议使用OSPF Version 2,针对IPv6协议使用OSPF Version 3。 在OSPF出现前,网络…

C++的list容器->基本概念、构造函数、赋值和交换、大小操作、插入和删除、数据存取、反转和排序、排序案例

#include<iostream> using namespace std; #include <list> //list容器构造函数 void printList(const list<int>& L) { for (list<int>::const_iterator it L.begin(); it ! L.end(); it) { cout << *it << "…

Unix与Linux区别

目录 历史和所有权 内核 发行版 开源性质 用户群体 命令行界面 历史和所有权 Unix&#xff1a; Unix是一个操作系统家族的名称&#xff0c;最早由贝尔实验室&#xff08;Bell Labs&#xff09;的肖像电机公司&#xff08;AT&T&#xff09;开发。最早的Unix版本是在19…

如何在MATLAB中创建一个矩阵?如何在MATLAB中执行矩阵运算?如何在MATLAB中绘制图形?

如何在MATLAB中创建一个矩阵&#xff1f; 在MATLAB中创建一个矩阵有多种方法。以下是一些常见的方法&#xff1a; 直接输入矩阵元素&#xff1a; 你可以直接在MATLAB命令窗口中输入矩阵的元素&#xff0c;每个元素之间用空格或逗号分隔&#xff0c;不同行之间用分号分隔。 A …

研究生摆烂摆烂的一个寒假

寒假&#xff1a;27-24&#xff0c;不到一个月 刚回家&#xff0c;不想学习&#xff0c;摆烂 想学了&#xff0c;又过年了&#xff0c;于是又开摆 又想学了&#xff0c;家里面有有点小事&#xff0c;于是又开摆 摆完&#xff0c;没想到就返校啦 期末作业没完成&#xff08…

C++ sort函数中自定义比较函数并传入变量、类的成员变量

在vector排序时候&#xff0c;有时候需要自定义排序&#xff0c;这个教程网上也很多&#xff0c;但是一般都是只使用了vector元素内部的变量&#xff0c;这里给出传入变量的实例代码 float camera_param_cx camera_param_cx_;float same_traffic_light_max_area same_traffic…

【深入理解设计模式】原型设计模式

原型设计模式 原型设计模式&#xff08;Prototype Pattern&#xff09;是一种创建型设计模式&#xff0c;它允许通过复制已有对象来创建新对象&#xff0c;而无需直接依赖它们的具体类。这种模式通常用于需要频繁创建相似对象的场景&#xff0c;以避免昂贵的创建操作或初始化过…

【办公类-16-10-02】“2023下学期 6个中班 自主游戏观察记录(python 排班表系列)

背景需求&#xff1a; 已经制作了本学期的中4班自主游戏观察记录表 【办公类-16-10-01】“2023下学期 中4班 自主游戏观察记录&#xff08;python 排班表系列&#xff09;-CSDN博客文章浏览阅读398次&#xff0c;点赞10次&#xff0c;收藏3次。【办公类-16-10-01】“2023下学…

Stable Diffusion 3的到来巩固了 AI 图像对抗 Sora 和 Gemini 的早期领先优势

Stability AI 将其更改为 Stable Diffusion 3。VentureBeat 报道称&#xff0c;Stability AI 的下一代旗舰 AI 图像生成模型将使用类似于 OpenAI 的 Sora 的扩散变压器框架。其当前模型仅依赖于扩散架构。虽然尚未发布&#xff0c;但您可以在等候名单中注册。 官方网址链接&am…

编程笔记 Golang基础 022 数组

编程笔记 Golang基础 022 数组 一、数组定义和初始化二、访问数组元素三、遍历数组四、数组作为参数六、特点七、注意事项 在Go语言中&#xff0c;数组是一种基本的数据结构&#xff0c;用于存储相同类型且长度固定的元素序列。 一、数组定义和初始化 // 声明并初始化一个整数…

Vue源码系列讲解——生命周期篇【三】(initLifecycle)

目录 1. 前言 2. initLifecycle函数分析 3. 总结 1. 前言 在上篇文章中&#xff0c;我们介绍了生命周期初始化阶段的整体工作流程&#xff0c;以及在该阶段都做了哪些事情。我们知道了&#xff0c;在该阶段会调用一些初始化函数&#xff0c;对Vue实例的属性、数据等进行初始…

Linux:Jenkins:GitLab+Maven+Jenkins的部署

1.环境 我这里准备了三台centos7 1.用于部署gitlab 运行内存&#xff1a;6G 名字&#xff1a;Jenkins-GitLab 192.168.6.1 2.用于部署jenkins 运行内存&#xff1a;2G 名字&#xff1a;Jenkins-server 192.168.6.2 3.用于打包测试…

【YOLO系列算法人员摔倒检测】

YOLO系列算法人员摔倒检测 模型和数据集下载YOLO系列算法的人员摔倒检测数据集可视化数据集图像示例&#xff1a; 模型和数据集下载 yolo行人跌倒检测一&#xff1a; 1、训练好的行人跌倒检测权重以及PR曲线&#xff0c;loss曲线等等&#xff0c;map达90%多&#xff0c;在行人跌…

招联金融VS兴业消金:洞察排头兵背后的秘密

来源 | 镭射财经&#xff08;leishecaijing&#xff09; 持牌消金历经十年浮沉&#xff0c;行业座次结构化调整加剧&#xff0c;但排头兵始终稳固如初。其中&#xff0c;招联金融和兴业消金长期位居榜首&#xff0c;以风向标姿态引领消金行业发展趋势。 从基因来看&#xff0…

【每日前端面经】2023-02-23

题目来源: 牛客 企业级开发整体流程有哪些 项目启动需求调研->需求文档系统设计->设计文档程序开发->开发文档BUG测试->测试文档验收维护 遇到技术难题怎么办 分析可能出现的原因查找搜索引擎寻问文心一言等对话模型打断点&#xff0c;寻找问题复现再一次归纳分…

.net6 webapi log4net完整配置使用流程

前置&#xff1a;为项目安装如下两个依赖 1.创建文件夹cfgFile 2.创建log4net.Config <?xml version"1.0" encoding"utf-8" ?> <log4net><appender name"ConsoleAppender" type"log4net.Appender.ConsoleAppender"…

月之暗面:Moonshot AI接口总结

前言&#xff1a; 开发者们只需访问 platform.moonshot.cn&#xff0c;便能创建自己的 API Key&#xff0c;进而将 Kimi 智能助手背后的同款 moonshot 模型能力&#xff0c;如长文本处理和出色的指令遵循等&#xff0c;集成至自己的产品中。这不仅增强了现有产品的功能&#x…

OkHttp 相关问题

1、OkHttp请求整体流程是怎么样? ​ Request-》OkHttpClient-》RealCall 同步 -》 在调用线程 执行五大拦截器 异步 -》 使用分发器将任务在线程池执行 五大拦截器 ---首先AsyncCall --加到等待队列readyAsyncCalls--》判断host有没有 已经存在。有,就赋值原来的。(reuseC…