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
vue3.0中setup使用(两种用法)
Dec 02 Vue.js
Vue router安装及使用方法解析
Dec 02 Vue.js
Vue在H5 项目中使用融云进行实时个人单聊通讯
Dec 14 Vue.js
vue中watch的用法汇总
Dec 28 Vue.js
vue 页面跳转的实现方式
Jan 12 Vue.js
vue实现轮播图帧率播放
Jan 26 Vue.js
vue-cropper组件实现图片切割上传
May 27 Vue.js
vue Element-ui表格实现树形结构表格
Jun 07 Vue.js
Vue图片裁剪组件实例代码
Jul 02 Vue.js
详解Vue中$props、$attrs和$listeners的使用方法
Feb 18 Vue.js
如何优化vue打包文件过大
Apr 13 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
实用函数8
2007/11/08 PHP
PHP中用header图片地址 简单隐藏图片源地址
2008/04/09 PHP
PHP根据传来的16进制颜色代码自动改变背景颜色
2014/06/13 PHP
php面向对象之反射功能与用法分析
2017/03/29 PHP
JSON PHP中,Json字符串反序列化成对象/数组的方法
2018/05/31 PHP
JS教程:window.location使用方法的区别介绍
2013/10/04 Javascript
onkeyup,onkeydown和onkeypress的区别介绍
2013/10/21 Javascript
5个JavaScript经典面试题
2014/10/13 Javascript
JS实现的鼠标跟随代码(卡通手型点击效果)
2015/10/26 Javascript
Vue报错:Uncaught TypeError: Cannot assign to read only property’exports‘ of object’#‘的解决方法
2017/06/17 Javascript
详解使用vue实现tab 切换操作
2017/07/03 Javascript
vue axios 给生产环境和发布环境配置不同的接口地址(推荐)
2018/05/08 Javascript
JavaScript事件冒泡与事件捕获实例分析
2018/08/01 Javascript
JavaScript引用类型RegExp基本用法详解
2018/08/09 Javascript
详解vue通过NGINX部署在子目录或者二级目录实践
2018/09/03 Javascript
React+Antd+Redux实现待办事件的方法
2019/03/14 Javascript
重学 JS:为啥 await 不能用在 forEach 中详解
2019/04/15 Javascript
Vue 权限控制的两种方法(路由验证)
2019/08/16 Javascript
js回溯法计算最佳旅行线路代码实例
2019/09/11 Javascript
js面向对象方式实现拖拽效果
2021/03/03 Javascript
Python的Flask框架中@app.route的用法教程
2015/03/31 Python
Python基于checksum计算文件是否相同的方法
2015/07/09 Python
浅谈Python实现Apriori算法介绍
2017/12/20 Python
使用python实现回文数的四种方法小结
2019/11/24 Python
python 图像的离散傅立叶变换实例
2020/01/02 Python
Python之Django自动实现html代码(下拉框,数据选择)
2020/03/13 Python
html5用video标签流式加载的实现
2020/05/20 HTML / CSS
百联网上商城:i百联
2017/01/28 全球购物
飞利浦法国官网:Philips法国
2019/07/10 全球购物
个人思想理论学习的自我鉴定
2013/11/30 职场文书
安全生产管理合理化建议书
2014/03/12 职场文书
艺术设计专业个人求职信
2014/04/10 职场文书
清洁工工作总结
2015/08/11 职场文书
2019年励志签名:致拼搏路上的自己
2019/10/11 职场文书
Apache Hudi的多版本清理服务彻底讲解
2022/03/31 Servers
Android学习之BottomSheetDialog组件的使用
2022/06/21 Java/Android