返回顶部
首页 > 资讯 > 前端开发 > JavaScript >Vue3纯前端实现Vue路由权限的方法详解
  • 502
分享到

Vue3纯前端实现Vue路由权限的方法详解

2024-04-02 19:04:59 502人浏览 薄情痞子
摘要

目录前言RBAC模型代码实现登录菜单信息动态路由筛选总结前言 在开发管理后台时,都会存在多个角色登录,登录成功后,不同的角色会展示不同的菜单路由。这就是我们通常所说的动态路由权限,实

前言

开发管理后台时,都会存在多个角色登录,登录成功后,不同的角色会展示不同的菜单路由。这就是我们通常所说的动态路由权限,实现路由权限的方案有多种,比较常用的是由前端使用addRoutes(V3版本改成了addRoute)动态挂载路由和服务端返回可访问的路由菜单这两种。今天主要是从前端角度,实现路由权限的功能。

RBAC模型

前端实现路由权限主要是基于RBAC模型。

RBAC(Role-Based Access Control)即:基于角色的权限控制。通过角色关联用户,角色关联权限的方式间接赋予用户权限。

代码实现

登录

首先是登录,登录成功后,服务端会返回用户登录的角色、token以及用户信息等。用户角色如:role: ['admin']。我们一般会将这些信息保存到Vuex里。

const login = () => {
  ruleFORMRef.value?.validate((valid: boolean) => {
    if (valid) {
      store.dispatch('userModule/login', { ...accountForm })
    } else {
      console.log('error submit!')
    }
  })
}

信息存储在Vuex:

async login({ commit }, payload: IRequest) {
  // 登录获取token
  const { data } = await accountLogin(payload)
  commit('SET_TOKEN', data.token)
  localCache.setCache('token', data.token)
  // 获取用户信息
  const userInfo = await getUserInfo(data.id)
  commit('SET_USERINFO', userInfo.data)
  localCache.setCache('userInfo', userInfo.data)
  router.replace('/')
},

服务端返回token:

服务端返回用户信息:

菜单信息

路由菜单信息分为两种,一种是默认路由constantRoutes,即所有人都能够访问的页面,不需去通过用户角色去判断,如login、404、首页等等。还有一种就是动态路由asyncRoutes,用来放置有权限(roles 属性)的路由,这部分的路由是需要访问权限的。我们最终将在动态路由里面根据用户角色筛选出能访问的动态路由列表。

我们将默认路由和动态路由都写在router/index.ts里。

import { createRouter, createWEBHashHistory, RouteRecordRaw } from 'vue-router'
const Layout = () => import('@/Layout')


export const constantRoutes: RouteRecordRaw[] = [
  {
    path: '/login',
    name: 'login',
    component: () => import('@/views/login/index.vue'),
    meta: {
      title: '登录',
      hidden: true
    }
  },
  {
    path: '/',
    component: Layout,
    redirect: '/analysis/dashboard',
    name: 'Analysis',
    meta: {
      hidden: false,
      icon: 'icon-home',
      title: '系统总览'
    },
    children: [
      {
        path: '/analysis/dashboard',
        name: 'Dashboard',
        component: () => import('@/views/analysis/dashboard/dashboard.vue'),
        meta: { title: '商品统计', hidden: false }
      },
      {
        path: '/analysis/overview',
        name: 'Overview',
        component: () => import('@/views/analysis/overview/overview.vue'),
        meta: { title: '核心技术', hidden: false }
      }
    ]
  },
  {
    path: '/product',
    component: Layout,
    redirect: '/product/cateGory',
    name: 'Product',
    meta: {
      hidden: false,
      icon: 'icon-tuijian',
      title: '商品中心'
    },
    children: [
      {
        path: '/product/category',
        name: 'Category',
        component: () => import('@/views/product/category/category.vue'),
        meta: { title: '商品类别', hidden: false }
      },
      {
        path: '/product/goods',
        name: 'Goods',
        component: () => import('@/views/product/goods/goods.vue'),
        meta: { title: '商品信息', hidden: false }
      }
    ]
  },
  {
    path: '/story',
    component: Layout,
    redirect: '/story/chat',
    name: 'Story',
    meta: {
      hidden: false,
      icon: 'icon-xiaoxi',
      title: '随便聊聊'
    },
    children: [
      {
        path: '/story/chat',
        name: 'Story',
        component: () => import('@/views/story/chat/chat.vue'),
        meta: { title: '你的故事', hidden: false }
      },
      {
        path: '/story/list',
        name: 'List',
        component: () => import('@/views/story/list/list.vue'),
        meta: { title: '故事列表', hidden: false }
      }
    ]
  },
  {
    path: '/404',
    component: () => import('@/views/404.vue'),
    meta: {
      title: 'Not Found',
      hidden: true
    }
  },
  {
    path: '/:pathMatch(.*)*',
    redirect: '/404',
    meta: {
      hidden: true,
      title: 'Not Found'
    }
  }
]

export const asyncRoutes: RouteRecordRaw[] = [
  {
    path: '/system',
    component: Layout,
    redirect: '/system/department',
    name: 'System',
    meta: {
      hidden: false,
      icon: 'icon-shezhi',
      title: '系统管理'
    },
    children: [
      {
        path: '/system/department',
        name: 'Department',
        component: () => import('@/views/system/department/department.vue'),
        meta: { title: '部门管理', hidden: false, role: ['admin'] }
      },
      {
        path: '/system/menu',
        name: 'Menu',
        component: () => import('@/views/system/menu/menu.vue'),
        meta: { title: '菜单管理', hidden: false, role: ['admin'] }
      },
      {
        path: '/system/role',
        name: 'Role',
        component: () => import('@/views/system/role/role.vue'),
        meta: { title: '角色管理', hidden: false, role: ['editor'] }
      },
      {
        path: '/system/user',
        name: 'User',
        component: () => import('@/views/system/user/user.vue'),
        meta: { title: '用户管理', hidden: false, role: ['editor'] }
      }
    ]
  }
]
const router = createRouter({
  history: createWebHashHistory(),
  routes: constantRoutes
})
export default router

我们将系统管理这个菜单作为动态路由部分,里面的子菜单meta属性下都分配有一个访问权限的role属性,我们需要将role属性和用户角色去匹配是否用户具有访问权限。

动态路由筛选

思路:

我们登录得到了用户角色role和写好路由信息(分为默认路由列表和动态路由列表),之后我们需要做的就是通过用户角色role去匹配动态路由列表里面每个子路由的role属性,得到能够访问的动态路由部分,将默认路由和我们得到的动态路由进行拼接这样我们就得到了用户能够访问的完整前端路由,最后使用addRoute将完整路由挂载到router上。

有了这样一个比较清晰的思路,接下来我们就来尝试着实现它。

我们可以将这块的逻辑也放在Vuex里面,在store/modules下新建一个permission.ts文件。

首先我们需要写一个方法去判断用户是否具有访问单个路由的权限:


const hasPermission = (roles: string[], route: any) => {
  if (route.meta && route.meta.roles) {
    return roles.some((role) => {
      if (route.meta?.roles !== undefined) {
        return route.meta.roles.includes(role)
      } else {
        return false
      }
    })
  } else {
    return true
  }
}

实现的核心是route.meta.roles.includes(role),即路由的roles是否包含了用户的角色,包含了就可以访问,否则不能。

对用户角色进行some遍历主要是用户的角色可能存在多个,如:['admin', 'editor']。

这样我们就实现了单个路由访问权限的筛选,但是动态路由列表是一个数组,每个一级路由下可能有二级路由、三级路由甚至更多,这样我们就需要用到递归函数进行筛选:


const filterAsyncRoutes = (routes: RouteRecordRaw[], roles: string[]) => {
  const res: RouteRecordRaw[] = []
  routes.forEach((route) => {
    const r = { ...route }
    if (hasPermission(roles, r)) {
      if (r.children) {
        r.children = filterAsyncRoutes(r.children, roles)
      }
      res.push(r)
    }
  })
  return res
}

这样,通过调用filterAsyncRoutes这个函数,然后传入utes:动态路由列表,roles:用户角色两个参数就能得到我们能访问的动态路由了。

然后我们将筛选得到的动态路由和默认路由通过concat拼接得到完整可访问路由,最后通过addRoute挂载。

我们将以上代码逻辑整理到sion.ts里:

import { Module } from 'vuex'
import { RouteRecordRaw } from 'vue-router'
import { constantRoutes, asyncRoutes } from '@/router'
import { IRootState } from '../types'
import router from '@/router'

const hasPermission = (roles: string[], route: any) => {
  if (route.meta && route.meta.roles) {
    return roles.some((role) => {
      if (route.meta?.roles !== undefined) {
        return route.meta.roles.includes(role)
      } else {
        return false
      }
    })
  } else {
    return true
  }
}

const filterAsyncRoutes = (routes: RouteRecordRaw[], roles: string[]) => {
  const res: RouteRecordRaw[] = []
  routes.forEach((route) => {
    const r = { ...route }
    if (hasPermission(roles, r)) {
      if (r.children) {
        r.children = filterAsyncRoutes(r.children, roles)
      }
      res.push(r)
    }
  })
  return res
}
interface IPermissionState {
  routes: RouteRecordRaw[]
  dynamicRoutes: RouteRecordRaw[]
}
export const routesModule: Module<IPermissionState, IRootState> = {
  namespaced: true,
  state: {
    routes: [],
    dynamicRoutes: []
  },
  getters: {},
  mutations: {
    SET_ROUTES(state, routes) {
      state.routes = routes
    },
    SET_DYNAMICROUTES(state, routes) {
      state.dynamicRoutes = routes
    }
  },
  actions: {
    generateRoutes({ commit }, { roles }) {
      // accessedRoutes: 筛选出的动态路由
      const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
      // 将accessedRoutes和默认路由constantRoutes拼接得到完整可访问路由
      commit('SET_ROUTES', constantRoutes.concat(accessedRoutes))
      commit('SET_DYNAMICROUTES', accessedRoutes)
      // 通过addRoute将路由挂载到router上
      accessedRoutes.forEach((route) => {
        router.addRoute(route)
      })
    }
  }
}

这样就实现了所有代码逻辑。有个问题,addRoute应该何时调用,在哪里调用?

登录后,获取用户的权限信息,然后筛选有权限访问的路由,再调用addRoute添加路由。这个方法是可行的。但是不可能每次进入应用都需要登录,用户刷新浏览器又要登录一次。所以addRoute还是要在全局路由守卫里进行调用。

我们在router文件夹下创建一个permission.ts,用于写全局路由守卫相关逻辑:

import router from '@/router'
import { RouteLocationNormalized } from 'vue-router'
import localCache from '@/utils/cache'
import NProgress from 'nprogress'
import 'nprogress/nprogress.CSS'
import store from '@/store'

NProgress.configure({ showSpinner: false })
const whiteList = ['/login']
router.beforeEach(
  async (
    to: RouteLocationNormalized,
    from: RouteLocationNormalized,
    next: any
  ) => {
    document.title = to.meta.title as string
    const token: string = localCache.getCache('token')
    NProgress.start()
    // 判断该用户是否登录
    if (token) {
      if (to.path === '/login') {
        // 如果登录,并准备进入 login 页面,则重定向到主页
        next({ path: '/' })
        NProgress.done()
      } else {
        const roles = store.state.userModule.roles
        store.dispatch('routesModule/generateRoutes', { roles })
        // 确保添加路由已完成
        // 设置 replace: true, 因此导航将不会留下历史记录
        next({ ...to, replace: true })
        // next()
      }
    } else {
      // 如果没有 token
      if (whiteList.includes(to.path)) {
        // 如果在免登录的白名单中,则直接进入
        next()
      } else {
        // 其他没有访问权限的页面将被重定向到登录页面
        next('/login')
        NProgress.done()
      }
    }
  }
)
router.afterEach(() => {
  NProgress.done()
})

这样,完整的路由权限功能就完成了。我们可以做一下验证:

动态路由

我们登录的用户角色为roles: ['editor'],动态路由为系统管理菜单,里面有四个子路由对应有roles,正常情况下我们可以访问系统管理菜单下的角色管理和用户管理。

渲染菜单界面

筛选出的动态路由

没有任何问题!

总结

前端实现动态路由是基于RBAC思想,通过用户角色去筛选出可以访问的路由挂载在router上。这样实现有一点不好的地方在于菜单信息是写死在前端,以后要改个显示文字或权限信息,需要重新修改然后编译。

到此这篇关于vue3纯前端实现Vue路由权限的文章就介绍到这了,更多相关Vue3纯前端实现路由权限内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Vue3纯前端实现Vue路由权限的方法详解

本文链接: https://lsjlt.com/news/148603.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Vue3纯前端实现Vue路由权限的方法详解
    目录前言RBAC模型代码实现登录菜单信息动态路由筛选总结前言 在开发管理后台时,都会存在多个角色登录,登录成功后,不同的角色会展示不同的菜单路由。这就是我们通常所说的动态路由权限,实...
    99+
    2024-04-02
  • 前端配合后端实现Vue路由权限的方法实例
    目录前言实现思路代码实现登录本地路由列表生成路由挂载路由总结前言 在开发管理后台时,都会存在多个角色登录,登录成功后,不同的角色会展示不同的菜单路由。这就是我们通常所说的动态路由权限...
    99+
    2024-04-02
  • vue2/vue3路由权限管理的方法实例
    1. Vue 路由权限控制一般有2种方法 a、路由元信息(meta) b、动态加载菜单和路由(addRoutes) 2 路由元信息(meta)来进行路由权限控制 2.1 在vue2种...
    99+
    2024-04-02
  • vue路由权限和按钮权限的实现示例
    目录一 菜单路由权限二 按钮权限的实现一 菜单路由权限 1.1前端路由配置表 1.2后端数据返回 1.3 拿到数据后存到vuex  1.4 扁平化的目的是为了跳转路由...
    99+
    2024-04-02
  • Vue实现两种路由权限控制方式
    目录方式一:路由元信息(meta)方式二:动态生成路由表(addRoutes)路由权限控制常用于后台管理系统中,对不同业务人员能够访问的页面进行一个权限的限制。 对于无权限的页面可...
    99+
    2024-04-02
  • vue前端路由以及vue-router两种模式实例详解
    目录前言1、什么是前端路由(1)后端路由阶段(2)前后端分离路由阶段(3)单页面应用阶段2、vue-router 提供了 hash 和 history 两种路由模式(1)hash模式...
    99+
    2024-04-02
  • 从vue-router看前端路由的两种实现
    目录模式参数 HashHistory HashHistory.push()HashHistory.replace()监听地址栏HTML5History 两种模式比较 his...
    99+
    2024-04-02
  • Vue3 中路由Vue Router 的使用实例详解
    目录前言:一、什么是 Vue Router ?二、路由的使用1、路由的安装2、路由的模式3、创建路由模块4、声明路由链接和占位符三、路由的重定向和别名四、嵌套路由五、声明式和编程式导...
    99+
    2023-02-18
    Vue3 路由Vue Router使用 Vue Router使用
  • vue导出word纯前端的实现方式
    vue导出word纯前端实现 最近项目有个需求导出word,纯前端实现,查了查资料,用docxtemplater简直不要太简单。 直接把官网例子拿过来就可以了。!!! 官网地址 首先...
    99+
    2024-04-02
  • Vue实现路由嵌套的方法实例
    1、嵌套路由又称子路由,在实际应用中,通常由多层嵌套的组件组合而成。(其实就是套娃操作罢了,跟后端的视图跳转路径蛮像的): 2、 创建用户信息组件,在 views/user 目录下...
    99+
    2024-04-02
  • 详解vue各种权限控制与管理的实现思路
    本篇文章给大家带来了关于vue的相关知识,其中主要详细介绍了vue各种权限控制与管理的实现思路,感兴趣的朋友下面一起来看一下吧,希望对大家有帮助。一、 菜单权限菜单权限:控制用户在系统中能够看到哪些菜单项菜单权限指的就是后台系统中的左侧的菜...
    99+
    2023-05-14
    前端 Vue.js Vuex
  • SpringSecurity动态权限的实现方法详解
    目录1. 动态管理权限规则1.1 数据库设计1.2 实战2. 测试最近在做 TienChin 项目,用的是 RuoYi-Vue 脚手架,在这个脚手架中,访问某个接口需要什么权限,这个...
    99+
    2024-04-02
  • Vuerouter的addRoute方法实现控制权限方法详解
    目录路由分为静态路由和动态路由静态路由和动态路由的优缺点动态路由实现思路动态路由遇到的问题与解决方式路由分为静态路由和动态路由 静态路由和动态路由的优缺点 1、中大型项目,采用的都是...
    99+
    2024-04-02
  • vue前端RSA加密java后端解密的方法实现
    目录一、前言二、前端代码与用法三、后端代码与用法一、前言 最近安全测试的总是测出安全漏洞来,让开发改。 想了想干脆把请求参数都加密下,前端加密后端解密,这样总差不多了。 看了下AES...
    99+
    2023-02-24
    vue RSA加密 java后端解密
  • 详解vue中路由的安装和使用方法
    Vue 是一款流行的前端开发框架,它提供了诸多方便开发的工具和功能。其中,Vue 路由能够帮助我们创建单页应用程序,并且帮助我们实现路由功能。本篇文章将会介绍 Vue 路由的使用。Vue路由安装在开始使用 Vue 路由之前,我们需要先安装 ...
    99+
    2023-05-14
  • Vue实现路由嵌套的方法是什么
    这期内容当中小编将会给大家带来有关Vue实现路由嵌套的方法是什么,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。嵌套路由又称子路由,在实际应用中,通常由多层嵌套的组件组合而成。(其实就是套娃操作罢了,跟后端...
    99+
    2023-06-25
  • vue动态路由的实现方法有哪些
    本篇内容介绍了“vue动态路由的实现方法有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!vue动态路由的两种实现方法:1、简单的角色路由...
    99+
    2023-07-04
  • VUE 路由动态加载:解锁无限可能,打造灵活的前端应用
    Vue 路由动态加载概述: Vue 路由动态加载是前端开发中的一项激动人心的技术,它允许您在需要时加载组件,从而减少应用程序的初始加载时间并提高性能。这意味着您的应用程序可以更快速地启动,并且不会在启动时加载所有不必要的组件。 如何使用...
    99+
    2024-02-08
    Vue 路由 动态加载 前端开发 JavaScript 组件 按需加载
  • Vue路由传参的三种方式实例详解
    目录Vue路由传参三种方式一、params传参二、路由属性配置传参:三、query传参区别特别注意总结Vue路由传参三种方式 params传参路由属性配置传参query传参 一、pa...
    99+
    2023-01-28
    vue路由的传参方式 vue路由传参的两种方式 vue参数传递方式
  • 前端vue3使用axios调用后端接口的实现方法
    目录前言:第一步:在src下创建一个http文件夹,创建一个config的js文件!第二步:在src下创建一个http文件夹,创建一个axios的js文件!第三步:在src下创建一个...
    99+
    2022-12-08
    vue使用axios调用后端接口 axios调用后端接口 vue调用后端接口
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作