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中音频wavesurfer.js的使用方法
Feb 20 Vue.js
浅析VUE防抖与节流
Nov 24 Vue.js
Vue实现点击当前行变色
Dec 14 Vue.js
8个非常实用的Vue自定义指令
Dec 15 Vue.js
Vue提供的三种调试方式你知道吗
Jan 18 Vue.js
vue项目支付功能代码详解
Feb 18 Vue.js
Vue中Object.assign清空数据报错的解决方案
Mar 03 Vue.js
Vue.js中v-for指令的用法介绍
Mar 13 Vue.js
vue+echarts实现多条折线图
Mar 21 Vue.js
vue中控制mock在开发环境使用,在生产环境禁用方式
Apr 06 Vue.js
详解Vue3使用axios的配置教程
Apr 29 Vue.js
el-table-column 内容不自动换行的解决方法
Aug 14 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
PHP XML操作类DOMDocument
2009/12/16 PHP
解析php中static,const与define的使用区别
2013/06/18 PHP
php使用$_POST或$_SESSION[]向js函数传参
2014/09/16 PHP
浅谈php中include文件变量作用域
2015/06/18 PHP
深入理解PHP之源码目录结构与功能说明
2016/06/01 PHP
对laravel的csrf 防御机制详解,及form中csrf_token()的存在介绍
2019/10/24 PHP
php设计模式之组合模式实例详解【星际争霸游戏案例】
2020/03/27 PHP
Javascript调试工具(下载)
2007/01/09 Javascript
javascript 带有滚动条的表格,标题固定,带排序功能.
2009/11/13 Javascript
Jquery 快速构建可拖曳的购物车DragDrop
2009/11/30 Javascript
基于jquery的滑动样例代码
2010/11/20 Javascript
使用jquery实现IE下按backspace相当于返回操作
2014/03/18 Javascript
jQuery固定浮动侧边栏实现思路及代码
2014/09/28 Javascript
WordPress中利用AJAX技术进行评论提交的实现示例
2016/01/12 Javascript
必备的JS调试技巧汇总
2016/07/20 Javascript
jQuery模拟完美实现经典FLASH导航动画效果【附demo源码下载】
2016/11/09 Javascript
JavaScript表单验证开发
2016/11/23 Javascript
微信公众平台开发教程(六)获取个性二维码的实例
2016/12/02 Javascript
JS实现禁止用户使用Ctrl+鼠标滚轮缩放网页的方法
2017/04/28 Javascript
Vue.js使用$.ajax和vue-resource实现OAuth的注册、登录、注销和API调用
2017/05/10 Javascript
BootStrap selectpicker后台动态绑定数据
2017/06/01 Javascript
详解JS数组Reduce()方法详解及高级技巧
2017/08/18 Javascript
解决ie img标签内存泄漏的问题
2017/10/13 Javascript
js 原生判断内容区域是否滚动到底部的实例代码
2017/11/15 Javascript
JS加密插件CryptoJS实现的Base64加密示例
2020/08/16 Javascript
el-input 标签中密码的显示和隐藏功能的实例代码
2019/07/19 Javascript
[02:08]我的刀塔不可能这么可爱 胡晓桃_1
2014/06/20 DOTA
Python写的服务监控程序实例
2015/01/31 Python
Python的条件表达式和lambda表达式实例
2019/01/31 Python
Django实现发送邮件找回密码功能
2019/08/12 Python
详解tensorflow之过拟合问题实战
2020/11/01 Python
python邮件中附加文字、html、图片、附件实现方法
2021/01/04 Python
印度首个本地在线平台:nearbuy
2019/03/28 全球购物
小学教师师德感言
2014/02/10 职场文书
安全施工标语
2014/06/07 职场文书
中国在我心中演讲稿
2014/09/13 职场文书