vue自定义指令和动态路由实现权限控制

功能概述:

  • 根据后端返回接口,实现路由动态显示
  • 实现按钮(HTML元素)级别权限控制

涉及知识点:

  • 路由守卫
  • Vuex使用
  • Vue自定义指令

导航守卫

前端工程采用Github开源项目Vue-element-admin作为模板,该项目地址:Github | Vue-element-admin

在Vue-element-admin模板项目的src/permission.js文件中,给出了路由守卫、加载动态路由的实现方案,在实现了基于不同角色加载动态路由的功能。我们只需要稍作改动,就能将基于角色加载路由改造为基于权限加载路由。

导航守卫:可以应用于在路由跳转时,对用户的登录状态或权限进行判断。项目中使用全局前置守卫。参考Vue官方文档:https://router.vuejs.org/zh/guide/advanced/navigation-guards.html

后台返回接口

权限系统后台采用基于角色的权限控制方案(role-based access control),如上图所示,
该用户信息接口将查询用户所具有的所有角色,再将这些角色的权限并集按照路由 - 操作整合在一起返回。在用户登录入系统后,我们从后台请求获得用户信息(个人信息 + 权限信息),作为全局属性储存在前端。不同权限的用户看到的页面不同,依赖于这些属性,它们决定了路由如何加载、页面如何渲染。

这种多个组件依赖一组属性的场景,Vue提供了VueX作为全局状态管理方案。

使用VueX存储权限信息

src/store/moudules目录下定义permission.js

1.定义异步方法,方法内部包含HTTP请求从后台拉取数据

import http from '../../axios';
async function getUserInfo() {
 const res = await http.getUserInfo();
 return res;
}

使用await关键字,保证执行顺序正确。这里是为了保证能拿到接口返回的内容,以便于下一步处理。

const actions = {
 getPermissions({ commit }) {
 return new Promise(resolve => {
  getUserInfo().then(res => {
  if (res) {
   let permissionList = res.permissionList;
   commit('SET_PERMISSIONS', permissionList);
   // 根据后台返回的路由,生成实际可以访问的路由
   let accessRoutes = filterAsyncRoutesByPermissions(asyncRoutes, permissionList);
   commit('SET_ROUTES', accessRoutes);
   commit('SET_USER_INFO', { name: res.name, accountName: res.accountName })
   resolve(accessRoutes);
  } else {
   resolve([]);
  }
  }).catch(() => resolve([]));
 })
 }
}

VueX中action定义异步方法。

2. 定义静态、动态路由

src/router/index.js

静态路由:

export const constantRoutes = [
 {
  path: '/redirect',
  component: Layout,
  hidden: true,
  children: [
   {
    path: '/redirect/:path(.*)',
    component: () => import('@/views/redirect/index'),
   },
  ],
 ,
 ...
 {
  path: '/404',
  component: () => import('@/views/error-page/404'),
  hidden: true,
 }
];

动态路由:

export const asyncRoutes = [
 {
  path: '/system',
  component: Layout,
  name: '系统管理',
  meta: { title: '系统管理', icon: 'el-icon-user', affix: true },
  children: [
   {
    path: '/system',
    component: () => import('@/views/management/system/Index'),
    meta: { title: '系统管理', icon: 'el-icon-setting', affix: true },
   },
  ],
 }
...
]

静态路由中定义了所有用户均可访问的路由,动态路由中定义了动态加载的路由。

3.根据权限过滤并排序路由

export function filterAsyncRoutesByPermissions(routes, menus) {
 const res = []
 routes.forEach(route => {
 const tmp = { ...route }
 let index = menus.map(menu => menu.url).indexOf(tmp.path);
 if (index != -1) {
  // 后端返回路由信息覆盖前端定义路由信息
  tmp.name = menus[index].name;
  // debugger;
  tmp.meta.title = menus[index].name;
  tmp.children.forEach(child => {
  if (child.path == tmp.path) {
   child.meta.title = tmp.meta.title;
  }
  })
  res.push(tmp)
 }
 });
 // 根据返回菜单顺序,确定路由顺序
 /**
 * TODO 子菜单排序
 */
 res.sort((routeA, routeB) => menus.map(menu => menu.url).indexOf(routeA.path) - menus.map(menu => menu.url).indexOf(routeB.path))
 return res
}

根据url匹配,匹配到url的路由则加入数组。最终用户可以访问的路由 = 允许访问的动态路由 + 不需要权限的静态路由。

4.src/permission.js中的处理逻辑

// 引入store
import store from './store';
const whiteList = ['/login', '/auth-redirect']; // no redirect whitelist

// 路由守卫
router.beforeEach(async (to, from, next) => {
 //start progress bar
 NProgress.start()
 if (hasToken) {
  if (to.path === '/login') {
   // ... 省略登出逻辑
   NProgress.done();
  } else {
   // 查看是否已缓存过动态路由
   const hasRoutes = store.getters.permission_routes && store.getters.permission_routes.length > 0;
   if (hasRoutes) {
    next();
   } else {
    try {
     const accessRoutes = await store.dispatch('permission/getPermissions');
     router.addRoutes(accessRoutes);
     const toRoute = accessRoutes.filter((route) => route.path == to.path);
     next({ path: toRoute.length > 0 ? toRoute[0].path : accessRoutes[0].path, replace: true });
    } catch (error) {
     next(`/login?redirect=${to.path}`);
     NProgress.done();
    }
   }
  }
 } else {
  if (whiteList.indexOf(to.path) !== -1) {
   // in the free login whitelist, go directly
   next();
  } else {
   next(`/login?redirect=${to.path}`);
   NProgress.done();
  }
 }
});

router.afterEach(() => {
 // finish progress bar
 NProgress.done();
});

以上是动态路由实现方案。

Vue支持自定义指令,用法类似于Vue原生指令如v-modelv-on等,网上查阅到的大部分细粒度权限控制方案都使用这种方法。下面将给出我的实现。

自定义指令

自定义指令 v-permission

src/directive/permission/index.js

import store from '@/store'
 export default {
 inserted(el, binding, vnode) {
 const { value } = binding
 const permissions = store.getters && store.getters.permissions;
 if (value) {
  // 获取当前所挂载的vue所在的上下文节点url
  let url = vnode.context.$route.path;
  let permissionActions = permissions[url];
  // console.log(permissionActions)
  const hasPermission = permissionActions.some(action => {
  if (value.constructor === Array) {
   // 或判断: 只要存在任1,判定为有权限
   return value.includes(action);
  } else {
   return action === value;
  }
  })
  if (!hasPermission) {
  el.parentNode && el.parentNode.removeChild(el)
  }
 } else {
  throw new Error(`need further permissions!`)
 }
 }
}

后端给出的权限数据是路由(url)与操作的对应Map,url可以通过将要挂载到的vnode属性拿到。这个方法有点类似于AOP,在虚拟元素挂载之后做判断,如果没有权限则从父元素上移除掉。
使用方法:

  • 举例一:单个按钮 (注意双引号套单引号的写法)

     <el-button @click.native.prevent="editUser(scope.row)" type="text" size="small" v-permission="'op_edit'">
            编辑
     </el-button>

  • 举例二:或判断(传入数组),只要拥有数组中一个权限,则保留元素,所有权限都没有,则移除。

在上一篇博客https://www.jb51.net/article/194361.htm
下拉菜单上增加控制:

相应数据定义中增加action属性。
该方法无法覆盖所有场景,所以依然给出相应工具类:

/**
 *
 * @param {*当前页面路由} url
 * @param {*操作code e.g op_add } value
 * @return true/false 是否有该项权限
 */
function checkPermission(url, value) {

 const permissions = store.getters && store.getters.permissions;
 let permissionActions = permissions[url];

 if (!permissionActions) {
  return false;
 }

 let hasPermission = permissionActions.some(action => {
  if (value.constructor === Array) {
   // 或判断: 只要存在任1,判定为有权限
   return value.includes(action);
  } else {
   return action === value;
  }
 });
 return hasPermission;

}

以上完成按钮粒度权限控制。

以上就是vue自定义指令和动态路由实现权限控制的详细内容,更多关于vue自定义指令和动态路由的资料请关注我们其它相关文章!

(0)

相关推荐

  • Vue路由守卫及页面登录权限控制的设置方法(两种)

    ①先在我们的登录页面存储一个登录数据 // 登录成功时保存一个登录状态: sessionStorage.setItem("flag", 1); ② 添加路由守卫 方法一: 直接在路由中添加 const router = new VueRouter({ ... }) // 路由守卫 router.beforeEach((to, from, next) => { // ... }) 方法二:当我们使用的是export default 方法时可以在main.js中添加 router.b

  • Vue 权限控制的两种方法(路由验证)

    下面介绍两种权限控制的方法: 路由元信息(meta) 动态加载菜单和路由(addRoutes) 路由元信息(meta) 如果一个网站有不同的角色,比如 管理员 和 普通用户 ,要求不同的角色能访问的页面是不一样的 这个时候我们就可以 把所有的页面都放在路由表里 ,只要 在访问的时候判断一下角色权限 .如果有权限就让访问,没有权限的话就拒绝访问,跳转到404页面 vue-router 在构建路由时提供了元信息 meta 配置接口,我们可以在元信息中添加路由对应的权限,然后在路由守卫中检查相关权限,

  • 使用VueRouter的addRoutes方法实现动态添加用户的权限路由

    最近做vue 单页项目涉及到多角色用户权限问题,不同的角色用户拥有不同的功能权限, 不同的功能权限对应的不同的页面 git: https://github.com/pch1024/dynamicRouter 举个例子: 角色A =>功能1 =>功能2 =>功能3 角色B =>功能1 =>功能4 =>功能5 第1步 定义默认路由和动态路由 //动态路由(所有角色的都在这里,我们都做好组件页面了所以我们一定有这个,防君子不防小人) export const dynamicR

  • Vue路由权限控制解析

    前言 本人在公司主要负责中后台系统的开发,其中路由和权限校验算是非常重要且最为基本的一环.实际开发项目中,关于登录和路由权限的控制参照了vue-element-admin这个明星项目,并在此基础上基于业务进行了整合,接下来我会以这个项目为例,仔细地剖析整个路由和权限校验的过程,也算是对这个知识点的一些总结. 项目总体目录结构 进入今天主题之前,我们先来梳理下整个项目,src目录下的. api: 接口请求 assets: 静态资源 components: 通用组件 directive: 自定义指令

  • vue路由守卫,限制前端页面访问权限的例子

    今天给大家写一篇关于vue校验登录状态,如果是非法登录就跳转到登录页面的逻辑 首先需要写一个路由守卫,它的原理是每次路由发生变化时触发具体写法如下: router.beforeEach((to, from, next) => { next() }) beforeEach函数有三个参数: to:即将进入的路由对象 from:当前导航即将离开的路由 next,进行管道中的一个钩子,如果执行完了,则导航的状态就是 confirmed (确认的):否则为false,终止导航. 使用案例 限制登陆功能,具

  • vue 设置路由的登录权限的方法

    index.js 将需要登录权限的路由设置meta属性 meta:{requireAuth:true}, main.js 在main.js内直接写对路由的验证 router.beforeEach((to, from, next) => { if (to.matched.some(record => record.meta.requireAuth)){ // 判断该路由是否需要登录权限 if (sessionStorage.getItem("access_token")) {

  • 关于Vue的路由权限管理的示例代码

    前言 曾经在工作上对 vue 路由权限管理这方面有过研究,这几天又看到了几篇相关的文章,再加上昨天电面中又再一次提及到,就索性整理了一下自己的一些看法,希望对大家有帮助. 实现 大体上实现的思路很简单,先上图: 无非是将路由配置按用户类型分割为 用户路由 和 基本路由,不同的用户类型可能存在不同的 用户路由,具体依赖实际业务. 用户路由: 当前用户所特有的路由 基本路由:所有用户均可以访问的路由 实现控制的方式分两种: 通过vue-router addRoutes方法注入路由实现控制 通过vue

  • vue路由权限校验功能的实现代码

    引言 做后台系统的时候,难免会有用户权限的判断.admin可以查看全部菜单,user只能查看部分菜单. 一开始接触这个需求的时候,完全是纯前端做的.在配置路由的时候,加一个roles的属性,通过判断用户的roles是否与路由的roles属性相匹配来作为显示隐藏的依据 { path: '/router', name: 'router', meta: { title: '标题', roles: ['admin','user'] }, component: index, children: [ { p

  • vue权限路由实现的方法示例总结

    使用全局路由守卫 实现 前端定义好路由,并且在路由上标记相应的权限信息 const routerMap = [ { path: '/permission', component: Layout, redirect: '/permission/index', alwaysShow: true, // will always show the root menu meta: { title: 'permission', icon: 'lock', roles: ['admin', 'editor']

  • vue iview实现动态路由和权限验证功能

    github上关于vue动态添加路由的例子很多,本项目参考了部分项目后,在iview框架基础上完成了动态路由的动态添加和菜单刷新.为了帮助其他需要的朋友,现分享出实现逻辑,欢迎一起交流学习. Github地址 iview-dynamicRouter 实现目标 客户端从服务端拿到路由和权限数据后,刷新项目的路由和菜单列表,并进行权限控制. 项目基础 基础框架: iview组件库官方模板项目 iview-admin 的template分支项目,此项目为 iview-admin 的基础框架代码.项目地

  • vue-router 控制路由权限的实现

    注意:vue-router是无法完全控制前端路由权限. 1.实现思路 使用vue-router实例函数addRoutes动态添加路由规则,不多废话直接上思维导图: 2.实现步骤 2.1.路由匹配判断 // src/router.js import Vue from 'vue'; import Store from '@/store'; import Router from 'vue-router'; import Cookie from 'js-cookie'; const routers =

  • vue addRoutes实现动态权限路由菜单的示例

    需求 最近接手一个后台管理系统,需要实现导航菜单从后台拉取的效果:根据登录用户的权限不同分别拉出来的导航菜单也不一样,另外可操作的界面也存在区别. 问题 因为后台管理系统是准备使用vue+vue-router+element-ui+vuex的搭配来做的,可是单页应用在进入页面之前就已经将vue-router实例化并且注入vue实例中了,所以在进入登录页面的时候旧没办法在重新定制路由了.接下来各种百之谷之,发现vue-router在2.0版本中提供了addRoutes方法添加路由,希望的曙光出现.

随机推荐