动态路由-菜单级权限控制

194 阅读3分钟

动态路由-菜单级权限控制

一、菜单级权限控制的背景和作用

菜单级权限控制是指在前端应用程序中,根据用户的角色或权限设置,对菜单进行可见性和可操作性的限制。它的背景可以追溯到许多企业和组织需要根据不同用户的身份和职责提供不同功能和操作权限的需求。

在一个典型的管理系统或应用中,存在不同级别的用户,比如管理员、普通用户、访客等。这些用户可能具有不同的职能和权限,因此只能看到并访问与其角色相匹配的菜单选项。通过菜单级权限控制,可以有效管理和控制用户在系统中的操作范围,确保数据的安全性和系统的稳定性。

菜单级权限控制的作用主要有以下几个方面:

  1. 数据安全:通过限制用户可见的菜单选项,可以防止未经授权的用户查看和操作敏感数据,提高数据安全性。
  2. 界面简洁:针对不同用户的角色和权限,动态展示相应的菜单选项,可以避免用户在界面上看到无关或不可操作的菜单,提升用户体验。
  3. 提高工作效率:根据用户的需求和职责,呈现相关的菜单选项,可以提高用户的工作效率和使用便捷性,减少操作复杂度。
  4. 系统稳定性:通过对菜单级别的权限控制,可以减少非法访问和误操作,降低系统崩溃、数据破坏等风险,保持系统的稳定运行。

二、核心代码

permission.js

import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title'

NProgress.configure({ showSpinner: false }) // NProgress Configuration

const whiteList = ['/login'] // no redirect whitelist

router.beforeEach(async (to, from, next) => {
  // start progress bar
  NProgress.start()

  // set page title
  document.title = getPageTitle(to.meta.title)

  // determine whether the user has logged in
  const hasToken = getToken()

  if (hasToken) {
    if (to.path === '/login') {
      // if is logged in, redirect to the home page
      next({ path: '/' })
      NProgress.done()
    } else {
      const hasGetUserInfo = store.getters.name
      if (hasGetUserInfo) {
        next()
      } else {
        try {
          // get user info
          let { userMenuList } = await store.dispatch('user/getInfo')  // 获取路由数据
          const asyncRouter = formatRouter(userMenuList) // 格式化处理服务端返回的路由
          let accessRoutes = await store.dispatch('permission/generateRoutes', asyncRouter) // 合并路由
          accessRoutes.push({ path: '*', redirect: '/404', hidden: true }) // 末尾插入404否则会导致刷新异常
          router.addRoutes(accessRoutes) // vue-Router提供的动态路由方法
          router.options.routes = router.options.routes.concat(accessRoutes) // 左侧菜单栏加入路由数据
          next({ ...to, replace: true }) // hook一下防止异常问题
        } catch (error) {
          // remove token and go to login page to re-login
          await store.dispatch('user/resetToken')
          Message.error(error || 'Has Error')
          next(`/login?redirect=${to.path}`)
          NProgress.done()
        }
      }
    }
  } else {
    /* has no token*/

    if (whiteList.indexOf(to.path) !== -1) {
      // in the free login whitelist, go directly
      next()
    } else {
      // other pages that do not have permission to access are redirected to the login page.
      next(`/login?redirect=${to.path}`)
      NProgress.done()
    }
  }
})

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

/**
 * 格式化路由
 * @param {Array} routerList 格式化路由
 * @returns
 */
export function formatRouter(routerList) {
  const router = []
  try {
    routerList.forEach((e) => {
      let e_new = {
        path: e.path,
        name: e.name,
        hidden: e.hidden,
        meta: { title: e.meta.title, icon: e.meta.icon },
        component:
          e.component == 'Layout' ? () => import('@/layout') : (resolve) => require([`@/views/${e.component}`], resolve)  //这里做个环境判断,再本地使用require,上线环境使用import()
      }
      if (e.children && e.children.length > 0) {
        const children = formatRouter(e.children)
        // 保存权限
        e_new = { ...e_new, children: children }
      } else {
        e_new = { ...e_new, children: [] }
      }
      if (e.redirect) {
        e_new = { ...e_new, redirect: e.redirect }
      }

      router.push(e_new)
    })
  } catch (error) {
    console.error(error)
    return []
  }
  return router
}

三、总结

实现思路是通过路由导航守卫实现前端权限控制。它首先检查用户是否已登录,然后根据用户角色和路由配置生成可访问的菜单和路由表。在用户进行页面导航时,会进行权限验证并跳转到目标路由页面。如果用户未登录,则会根据白名单判断是否放行或重定向到登录页面。整体流程确保了用户在具备相应权限的情况下才能访问对应页面,增强了系统的安全性。

以上是一个比较简单的,不涉及到颗粒化比如接口鉴权,按钮级别鉴权等