前端配合后端实现Vue路由权限的方法实例

目录
  • 前言
  • 实现思路
  • 代码实现
    • 登录
    • 本地路由列表
    • 生成路由
    • 挂载路由
  • 总结

前言

在开发管理后台时,都会存在多个角色登录,登录成功后,不同的角色会展示不同的菜单路由。这就是我们通常所说的动态路由权限,实现路由权限的方案有多种,比较常用的是由前端使用addRoutes(V3版本改成了addRoute)动态挂载路由和服务端返回可访问的路由菜单这两种。上一篇文章讲了纯前端实现路由权限,没看过的可以点击文章链接纯前端实现Vue路由权限。今天主要是基于后端返回路由菜单的基础上,实现路由权限功能。

实现思路

后端返回路由菜单主要是在我们登录之后,后端接口会直接返回当前用户可访问的完整路由菜单,相当于前端基于RBAC模型筛选出了前端可访问的路由列表。

需要注意的是,后端返回的路由菜单是不包括login、404等页面的。前端这边还是需要写一份完整的路由列表,基于后端返回的可访问路由菜单去筛选出需要挂载在router上的路由列表。

代码实现

登录

首先是登录,登录成功后,服务端会返回登录用户可访问的路由菜单userMenus,我们一般会将这些信息保存到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)
  // 获取菜单
  const userMenu = await getUserMenu(userInfo.data.role.id)
  commit('SET_USERMENU', userMenu.data)
  localCache.setCache('userMenu', userMenu.data)
  router.replace('/main/analysis/dashboard')
},

接口返回的路由菜单信息:

路由菜单

可以看到,返回的userMenus是一个数组,包含了图标icon、路由名称name、路由地址、子路由children、路由type等重要信息。前面这些信息主要是用于遍历生成页面左侧的菜单列表,路由type则是用于后面筛选出需要挂载在router上的路由列表。

本地路由列表

前端这边还是需要写一份完整的路由列表,我这里打算在router/index.ts里面写入接口不返回的菜单,如login、404等页面。将接口可能返回的菜单单独放在router/main下面。

router/index.ts:

import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
const routes: RouteRecordRaw[] = [
  {
    path: '/',
    redirect: '/main'
  },
  {
    path: '/login',
    name: 'login',
    component: () => import('@/views/login/index.vue'),
    meta: {
      title: '登录'
    }
  },
  {
    path: '/main',
    name: 'main',
    redirect: '/main/analysis/dashboard',
    component: () => import('@/views/main/index.vue'),
    meta: {
      title: '核心技术'
    }
  },
  {
    path: '/:pathMatch(.*)*',
    name: 'notFound',
    component: () => import('@/views/404.vue'),
    meta: {
      title: '页面找不到~'
    }
  }
]

router/main下面就是写入所有菜单列表:

单个菜单内容,如dashboard.ts:

const dashboard = () => import('@/views/main/analysis/dashboard/dashboard.vue')
export default {
  path: '/main/analysis/dashboard',
  name: 'dashboard',
  component: dashboard,
  meta: {
    title: '商品统计'
  },
  children: []
}

整个router目录:

router目录

接下来,我们就需要根据userMenus去过滤我们写好的router/main下面的路由,也就是接口返回的菜单列表对应一份路由列表,然后将路由列表挂载在router上,这样就能访问路由了。

生成路由

现在我们需要根据userMenus生成对应的路由。

首先我们需要去加载所有的路由,也就是router/main下面的路由文件内容。这里我使用的是require.context方法来加载所有的路由。

这里简单介绍下require.context这个api:

require.context 是webpack的一个api,通过执行require.context()函数,来获取指定的文件夹内的特定文件,在需要多次从同一个文件夹内导入的模块,使用这个函数可以自动导入,不用每个都显示的写import来引入。

require.context(directory,useSubdirectories,regExp) 需要的参数:

  • directory:要搜索文件的相对路径
  • useSubdirectories:是否查询其子目录
  • regExp:匹配基础组件文件名的正则表达式

我们就通过这个api来加载router/main下面的路由。

const routeFiles = require.context('../router/main', true, /.ts/)

我们对routeFiles进行打印:

routeFiles

得到了一个对象,我们需要对这个对象进行遍历拿到文件内容:

routeFiles.keys().forEach((key) => {
  const route = require('../router/main' + key.split('.')[1]).default
  console.log(route)
  allRoutes.push(route)
})

打印得到route

route

这样我们就得到了所有的路由,放在allRoutes里面。

接下来我们需要根据userMenus获取需要添加的routes。

开始我们提到过路由type,这个字段主要是区分菜单下是否还有子菜单,1表示有子菜单,2表示没有子菜单。

接口返回的菜单

我们将allRoutes进行遍历,然后根据path与接口返回的菜单列表userMenus里的path进行比较,如果相同就是匹配到了,那我们就需要这条路由,否则就将这条路由过滤掉。由于allRoutes下的每一项都还可能存在子路由,所以这里我们也需要进行递归筛选。具体的方法如下:

const _recurseGetRoute = (menus: any[]) => {
  for (const menu of menus) {
    if (menu.type === 2) {
      const route = allRoutes.find((route) => route.path === menu.url)
      if (route) routes.push(route)
    } else {
      _recurseGetRoute(menu.children)
    }
  }
}

最终,routes就是我们得到的userMenus所对应的路由列表。

将生成对应的路由的逻辑整理如下:

import { RouteRecordRaw } from 'vue-router'

export function generateRoutes(userMenus: any[]): RouteRecordRaw[] {
  const routes: RouteRecordRaw[] = []
  // 1.先去加载默认所有的routes
  const allRoutes: RouteRecordRaw[] = []
  const routeFiles = require.context('../router/main', true, /.ts/)
  routeFiles.keys().forEach((key) => {
    const route = require('../router/main' + key.split('.')[1]).default
    console.log(route)
    allRoutes.push(route)
  })
  // 2.根据菜单获取需要添加的routes
  // userMenus:
  // type === 1 -> children -> type === 1
  // type === 2 -> url -> route
  const _recurseGetRoute = (menus: any[]) => {
    for (const menu of menus) {
      if (menu.type === 2) {
        const route = allRoutes.find((route) => route.path === menu.url)
        if (route) routes.push(route)
      } else {
        _recurseGetRoute(menu.children)
      }
    }
  }
  _recurseGetRoute(userMenus)
  return routes
}

挂载路由

最后,需要将我们得到的routes挂载er上面。

还是将挂载路由的时机放在全局路由守卫这里,我们在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']
const userMenu = store.state.userModule.userMenu
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 {
        store.dispatch('routesModule/generateRoutes', { userMenu })
        // 确保添加路由已完成
        // 设置 replace: true, 因此导航将不会留下历史记录
        next({ ...to, replace: true })
      }
    } else {
      // 如果没有 token
      if (whiteList.includes(to.path)) {
        // 如果在免登录的白名单中,则直接进入
        next()
      } else {
        // 其他没有访问权限的页面将被重定向到登录页面
        next('/login')
        NProgress.done()
      }
    }
  }
)
router.afterEach(() => {
  NProgress.done()
})

routesModule文件下的代码:

// 引入generateRoutes
import { generateRoutes } from '@/utils/generateRoutes'
actions: {
  generateRoutes({ commit }, { userMenu }) {
    const routes = generateRoutes(userMenu)
    // 将routes => router.main.children
    routes.forEach((route) => {
      router.addRoute('main', route)
    })
  }
}

这样,完整的路由权限功能就完成了。我们可以看一下页面:

系统界面

总结

相比纯前端实现路由权限,这种基于后端返回路由菜单的方式会显得简单一些。我们不需要经过RBAC去过滤出用户可以访问的路由,而是接口直接返回给了我们。我们只需要将路由菜单对应生成一份路由,然后将路由进行挂载。

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

(0)

相关推荐

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

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

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

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

  • vuex根据不同的用户权限展示不同的路由列表功能

    需求描述 最近接到一个新的需求,要求将系统的用户进行分类,用户登陆后根据不同的用户权限展示不同的功能列表. 这个功能在后台管理中很常见,大致的思路是 后台返回用户类型,前端根据用户类型生成该类用户可以访问的功能列表. 后台返回功能列表,前端进行循环渲染. 一个在前端生成功能列表,一个在后端返回,两个本质上类似,最终都是需要得到一个该用户的功能功能列表.但是两者都有一个不可忽视的东西,就是如果用户直接在地址栏输入会怎么样. 技术选型 由于公司项目不算小,为了后期维护方便,我还是选择了使用 vuex

  • 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权限路由实现的方法示例总结

    使用全局路由守卫 实现 前端定义好路由,并且在路由上标记相应的权限信息 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路由权限的方法实例

    目录 前言 实现思路 代码实现 登录 本地路由列表 生成路由 挂载路由 总结 前言 在开发管理后台时,都会存在多个角色登录,登录成功后,不同的角色会展示不同的菜单路由.这就是我们通常所说的动态路由权限,实现路由权限的方案有多种,比较常用的是由前端使用addRoutes(V3版本改成了addRoute)动态挂载路由和服务端返回可访问的路由菜单这两种.上一篇文章讲了纯前端实现路由权限,没看过的可以点击文章链接纯前端实现Vue路由权限.今天主要是基于后端返回路由菜单的基础上,实现路由权限功能. 实现思

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

    目录 前言 RBAC模型 代码实现 登录 菜单信息 动态路由筛选 总结 前言 在开发管理后台时,都会存在多个角色登录,登录成功后,不同的角色会展示不同的菜单路由.这就是我们通常所说的动态路由权限,实现路由权限的方案有多种,比较常用的是由前端使用addRoutes(V3版本改成了addRoute)动态挂载路由和服务端返回可访问的路由菜单这两种.今天主要是从前端角度,实现路由权限的功能. RBAC模型 前端实现路由权限主要是基于RBAC模型. RBAC(Role-Based Access Contr

  • vue路由权限和按钮权限的实现示例

    目录 一 菜单路由权限 二 按钮权限的实现 一 菜单路由权限 1.1前端路由配置表 1.2后端数据返回 1.3 拿到数据后存到vuex 1.4 扁平化的目的是为了跳转路由时进行对比权限 //扁平化方法 flatten(data) { return data.reduce((arr,{name,id,resourceType,dimensionTypeCode,btnPermissions,path,children = [],}) => arr.concat([{name,id,resource

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

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

  • Vue路由权限控制解析

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

  • 使用 webpack 插件自动生成 vue 路由文件的方法

    一款自动生成 vue 路由文件的 webpack 插件 vue-route-webpack-plugin在项目中试点成功了,现在在项目中已经不需要再维护路由配置文件了,由插件自动生成,节省了大家维护路由的时间. 从长远来看,使用插件自动生成路由是具有一定好处的.当项目中的路由配置非常多的时候,为了区分业务,你可能需要分成许多个文件来存放这些路由文件,这样就不得不去维护这些路由文件. 比如这样的路由文件结构: |-src/ |-router/ index.js childrenRouter.js

  • Vue实现路由嵌套的方法实例

    1.嵌套路由又称子路由,在实际应用中,通常由多层嵌套的组件组合而成.(其实就是套娃操作罢了,跟后端的视图跳转路径蛮像的): 2. 创建用户信息组件,在 views/user 目录下创建一个名为 Profile.vue 的视图组件: Profile.vue <template> <h1>咸鱼_翻身1</h1> </template> <script> export default { name: "UserList" } <

  • vue使用websocket的方法实例分析

    本文实例讲述了vue使用websocket的方法.分享给大家供大家参考,具体如下: 最近项目需要使用到websocket 但是框架是vue  网上查阅很多资料 vue-websocket 老是连接不上 索性就不适用封装的插件了,直接使用原生的websocket  我这边需求是 只需要接受就好 不需要发送 代码如下: 爬坑之路:vue里面this指向问题 第一版 使用原生js mounted(){ console.log(this)---------------------------------

  • VUE路由动态加载实例代码讲解

    首先新建vue工程,一般我们不会特殊处理路由,但当项目页面越来越多,路由配置也会越来越大,路由文件就会变得不好维护 import Vue from 'vue' import Router from 'vue-router' import HelloWorld from '@/components/HelloWorld' import Home from '@/components/Home' import Test1 from './test1.router.js' import Test2 f

  • SpringBoot Java后端实现okhttp3超时设置的方法实例

    目录 前言 导入 okhttp3方法简介 两种版本超时设置用法 总结 前言 okhttp是一个处理网络请求的开源项目,是安卓端最火热的轻量级框架,由移动支付Square公司开发.OkHttp 是一个默认高效的 HTTP 客户端.OkHttp3是Java和Android都能用,Android还有一个著名网络库叫Volley,那个只有Android能用. okttp3的github官方地址是:官方地址 HTTP/2 支持允许对同一主机的所有请求共享一个套接字. 连接池减少了请求延迟(如果 HTTP/

随机推荐