Vue 简单实现前端权限控制的示例


Posted in Vue.js onDecember 25, 2020

简要说明

最近写了一下vue控制权限(菜单、路由)的项目,用了vuex、addRoutes动态添加路由方法等,总共100多行代码,跟大家分享一下~

逻辑梳理

  • 除登录接口、退出接口外,其余接口增加token验证。
  • 打开页面时请求获取菜单接口,请求不成功说明未登录,给route默认添加login页面以及 * 重定向。
  • 登录成功后获取到token,把token存入session以及请求头。
  • 登录成功后获取菜单接口,请求回来的路由和vuex里面全部的路由进行匹配,获取component。
  • 把获取完component的路由格式化,找自己的parentId,如果找到的话插入到该元素的child里面。

思路大致就是这样,有听得模糊的也不要紧,跟随我的步伐看看代码是怎样写的你就明白了~

实现

1.初始化

route.js

import Vue from 'vue'
import Router from 'vue-router'
import store from '@/store'

Vue.use(Router)
const router = new Router()
// 全局前置守卫
router.beforeEach( async (to, from, next) => {
 let userRoutes = store.state.global.userRoutes //userRoutes 当前用户拥有的权限
 if (userRoutes.length && !userRoutes.filter(item => item.path == to.path).length) {
  next(from.path)
  return
 }
 next()
})
export default router

大家可以看到route.js里没有路由,因为路由都是动态添加进去的,只有一个全局守卫,作用是当登陆成功后,用户地址栏手动输入地址,判断路由是否正确,如果正确就让他跳转。

vuex

//state.js
export default {
 // 全部路由
 allRoutes: [
  //登录页面
  {
   path: '/demo',
   name: 'demo',
   component: () => import('@/views/demo')
  },
  {
   path: '*',
   redirect: '/demo'
  },
  //主页面
  {
   path: '/',
   component: () => import('@/views'),
  },
  {
   path: '/home',
   name: 'home',
   component: () => import('@/views/home')
  }
 ],
 // 用户匹配的路由,要用addRoutes添加到route
 userRoutes: [],
 // 渲染用户菜单
 userMenus: []
}

state中需要定义全部的路由,这个用来跟后台请求到的权限进行匹配,并且获取component组件。

actions.js里面是主要的逻辑,其中getMenu方法是本文的核心

数据返回时格式
menu = [
 {id: 1, name: '首页', path: '/home', parentId: 0},
 {id: 2, name: '系统设置', path: '', parentId: 0},
 {id: 3, name: '角色配置', path: '/roles', parentId: 2},
 {id: 4, name: '用户配置', path: '/users', parentId: 2}
]
需要处理成
menu = [
 {id: 1, name: '首页', path: '/home', parentId: 0},
 {id: 2, name: '系统设置', path: '', parentId: 0, 
   child: [
     {id: 3, name: '角色配置', path: '/roles', parentId: 2},
     {id: 4, name: '用户配置', path: '/users', parentId: 2}
    ]
  },
]

所以在下方需要用到递归来处理↓

//actions.js

// 获取当前用户权限
 getMenu: async ({ state, commit, dispatch }) => {
  //请求当前用户所拥有的权限
  let result = await axios('/api/menu/find')
  if (result.data.code > 0) {
   let userRoutes = result.data.result
   userRoutes.forEach(item => {
    item.child = []
    state.allRoutes.forEach(res => {
     if (item.path == res.path) {
      item.component = res.component
     }
    })
   })
   let oneArr = [], anotherArr = []  //oneArr 一级路由 anotherArr 其他级别路由
   oneArr = userRoutes.filter(item => !item.parentId)
   anotherArr = userRoutes.filter(item => item.parentId)
   anotherArr.forEach(item => {
    oneArr.forEach(obj => {
     if (item.parentId == obj.id) { //如果匹配 说明找到父级,直接push到父级的child里面
      if (!obj.child.filter(k => k.id == item.id).length) {
       obj.child.push(item)
      }
     } else {  //如果没有,说明本级路由没有找到,去下一级别路由找父级 路由级别:1级路由,2级路由,3级路由......
      dispatch('recurrArr', {arr: oneArr, items: item})
     }
    })
   })
   commit('setState', {state: 'userRoutes', value: userRoutes})
   commit('setState', {state: 'userMenus', value: oneArr})
   return {code: 1, data: userRoutes}  //处理完成后返回 oneArr是递归处理后嵌套的,userRoutes是获取到component来渲染route的
  } else {
   return {code: 0}
  }
 },
 // 递归找自己的parentId
 recurrArr: ({dispatch}, {arr, items}) => {
  if (!arr) {
   return
  }
  for(let i = 0; i < arr.length; i ++ ){
   let item = arr[i]
   if (item.id == items.parentId) {
    if (!item.child.filter(k => items.id == k.id).length) {
     item.child.push(items)
    }
    break;
   } else {
    dispatch('recurrArr', {arr: item.child, items: items})
   }
  }
 }

到现在为止,路由以及菜单的数据,就已经处理完了,剩下的就是addRoutes添加到route路由里面,这样页面就可以跳转了~

我们接着来看登录:

// 登录
 login: async ({ commit, dispatch}, params) => {
  let result = await axios('/api/login', {params})
  if(result.data.code > 0) {
   // 登录成功以后 获取当前用户权限路由
   let userRoutes = await dispatch('getMenu')
   if (userRoutes.code > 0) {
    // 把请求回来的路由动态添加到 route 里
    router.addRoutes(userRoutes.data)
    // 添加完成后,现在可以跳转到首页了~
    router.push('/home')
   }
   return {code: 1, data: result.data}
  } else {
   console.log(result.data.msg)
   return {code: 0}
  }
 },

现在就已经大功告成了,你的项目可以进行正常的登录、跳转、动态更新路由等操作了~

但是

现在还差最后一步,退出登录

因为在axios拦截里面,token失效后会调用退出接口

axios.interceptors.response.use(
 response => {
  if (response.status === 200) {
   // 身份验证失败 
   if(response.data.code === -1) {
    // 执行退出登录
    store.dispatch('global/loginOut')
   } else {
    // 如果请求头里有token
    let token = response.headers.token
    if(token) {
     localStorage.setItem('token', token)
     axios.defaults.headers.token = token
    }
   }
   return Promise.resolve(response)
  } else {
   return Promise.reject(response)
  }
 }
)

所以才会提到开头说:刚打开页面的时候,不管有没有登录,都去请求菜单接口。

如果没有登录,则会调用退出登录接口,给路由设置默认路由。

// 退出登录
 loginOut: async ({ state }) => {
  // 退出登录清空 token 和 headers 里面的 token
  localStorage.removeItem('token')
  delete axios.defaults.headers.token
  // 退出登录要动态添加 登录页面 和 * 重定向页面
  let errRoutes = state.allRoutes.filter(item => item.path == '*')
  errRoutes.push(state.allRoutes.filter(res => errRoutes[0].redirect == res.path)[0])
  router.addRoutes(errRoutes);
  router.currentRoute.path !== '/demo' ? router.push("/demo") : null
 
  let result = await axios('/api/loginOut', {params: {userId: state.userId}})
  if(result.data.code !== 1) {
   console.log('退出登录接口异常')
  }
 },

到现在为止,有项目就算大功告成了~

到此这篇关于Vue 简单实现前端权限控制的示例的文章就介绍到这了,更多相关Vue 前端权限控制内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Vue.js 相关文章推荐
在vue中通过render函数给子组件设置ref操作
Nov 17 Vue.js
vue+iview实现分页及查询功能
Nov 17 Vue.js
Vue如何循环提取对象数组中的值
Nov 18 Vue.js
vue 使用class创建和清除水印的示例代码
Dec 25 Vue.js
Vue中引入svg图标的两种方式
Jan 14 Vue.js
Vite和Vue CLI的优劣
Jan 30 Vue.js
如何使用vue3打造一个物料库
May 08 Vue.js
详解gantt甘特图可拖拽、编辑(vue、react都可用 highcharts)
Nov 27 Vue.js
Vue.js中v-bind指令的用法介绍
Mar 13 Vue.js
vue自定义右键菜单之全局实现
Apr 09 Vue.js
如何vue使用el-table遍历循环表头和表体数据
Apr 26 Vue.js
Vue2项目中对百度地图的封装使用详解
Jun 16 Vue.js
Vue通过阿里云oss的url连接直接下载文件并修改文件名的方法
Dec 25 #Vue.js
vue使用require.context实现动态注册路由
Dec 25 #Vue.js
vue 使用rules对表单字段进行校验的步骤
Dec 25 #Vue.js
vue 实现基础组件的自动化全局注册
Dec 25 #Vue.js
vue 使用class创建和清除水印的示例代码
Dec 25 #Vue.js
基于vue+echarts数据可视化大屏展示的实现
Dec 25 #Vue.js
vue3使用vue-count-to组件的实现
Dec 25 #Vue.js
You might like
火车采集器 免费版使出收费版本功能实现原理
2009/09/17 PHP
php笔记之:数据类型与常量的使用分析
2013/05/14 PHP
php遍历文件夹下的所有文件和子文件夹示例
2014/03/20 PHP
php结合ajax实现赞、顶、踩功能实例
2014/05/12 PHP
ThinkPHP中create()方法自动验证实例
2017/04/26 PHP
Laravel框架集成UEditor编辑器的方法图文与实例详解
2019/04/17 PHP
JavaScript图片轮播代码分享
2015/07/31 Javascript
JavaScript+html5 canvas实现本地截图教程
2020/04/16 Javascript
通过javascript进行UTF-8编码的实现方法
2016/06/27 Javascript
jQuery实现限制文本框的输入长度
2017/01/11 Javascript
EasyUI修改DateBox和DateTimeBox的默认日期格式示例
2017/01/18 Javascript
使用vue.js编写蓝色拼图小游戏
2017/03/17 Javascript
js实现自动图片轮播代码
2017/03/22 Javascript
Vue.js实战之Vuex的入门教程
2017/04/01 Javascript
jQuery Ajax使用FormData上传文件和其他数据后端web.py获取
2017/06/11 jQuery
CSS3+JavaScript实现翻页幻灯片效果
2017/06/28 Javascript
深入探究angular2 UI组件之primeNG用法
2017/07/26 Javascript
详解Vue组件实现tips的总结
2017/11/01 Javascript
vue环境搭建简单教程
2017/11/07 Javascript
微信小程序自定义底部导航带跳转功能
2018/11/27 Javascript
详解vue使用插槽分发内容slot的用法
2019/03/28 Javascript
微信小程序getLocation 需要在app.json中声明permission字段
2020/03/03 Javascript
Element Rate 评分的使用方法
2020/07/27 Javascript
vue:el-input输入时限制输入的类型操作
2020/08/05 Javascript
如何用python整理附件
2018/05/13 Python
解决Matplotlib图表不能在Pycharm中显示的问题
2018/05/24 Python
python使用KNN算法识别手写数字
2019/04/25 Python
Python代码使用 Pyftpdlib实现FTP服务器功能
2019/07/22 Python
Django使用消息提示简单的弹出个对话框实例
2019/11/15 Python
Python 实现将数组/矩阵转换成Image类
2020/01/09 Python
美国最大的珠宝首饰网上商城:Jewelry.com
2016/07/22 全球购物
财务管理职业生涯规划书
2014/02/26 职场文书
研究生考核个人自我鉴定
2014/03/27 职场文书
奠基仪式致辞
2015/07/30 职场文书
高三数学复习备考教学反思
2016/02/18 职场文书
十大最强火系宝可梦,喷火龙上榜,第一名有双火属性
2022/03/18 日漫