动态路由权限

6 阅读2分钟

实现思路

未命名绘图.png

技术实现

VUE3

路由入口

路径:@/router/index.js

import { createRouter, createWebHistory } from 'vue-router'
//静态路由表
import { static_router } from './utils/static_router'
//动态路由表
import { dynamic_router } from './utils/dynamic_router'
//白名单
import { Whitelist } from './utils/white_list'
import { useUserInfo } from "@/stores/user";

import { routerFilter } from './utils/router_filter';

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: static_router
})
router.beforeEach((to, from, next) => {
  const userInfoStore = useUserInfo();
  const token = userInfoStore.token
  const role = userInfoStore.userInfo.role
  if (token) {
    if (!userInfoStore.final_dyn_router.length) {
      userInfoStore.setFinalDYNRouter(routerFilter(role, dynamic_router))
      userInfoStore.final_dyn_router.forEach((route) => {
        router.addRoute(route)
      })
      router.addRoute({ path: "/:pathMatch(.*)*",name:"redirect", redirect: "/404",meta: { isDyn: true } })
      console.log(router.getRoutes())
      next({ ...to, replace: true })
    } else {
        next()
    }
  } else {
    if (Whitelist.includes(to.path)) {
      next()
    } else {
      next("/login")
    }
  }
})

export default router

动态路由

路径:@/router/utils/dynamic_router.js

//动态路由表
export const dynamic_router = [
  {
    path: '/dashboard',
    name: 'Dashboard',
    component: () => import('@/views/Dashboard.vue'),
    meta: { 
      title: '仪表盘', 
      auth: true,        // 需要登录
      isDyn:true,
      roles: ['admin', 'user', 'guest'] // 所有角色均可访问
    }
  },
  {
    path: '/user',
    name: 'UserManage',
    component: () => import('@/views/UserManage.vue'),
    meta: { 
      title: '用户管理', 
      auth: true,
      isDyn:true,
      roles: ['admin'] // 仅管理员可访问
    }
  },
  {
    path: '/settings',
    name: 'Settings',
    component: () => import('@/views/Settings.vue'),
    meta: { 
      title: '系统设置', 
      auth: true,
      isDyn:true,
      roles: ['admin', 'user'] // 管理员和普通用户可访问
    }
  },
  {
    path: '/profile',
    name: 'Profile',
    component: () => import('@/views/Profile.vue'),
    meta: { 
      title: '个人资料', 
      auth: true,
      isDyn:true,
      roles: ['admin', 'user', 'guest'] // 所有登录用户可访问
    }
  },
  {
    path: '/guest-only',
    name: 'GuestOnly',
    component: () => import('@/views/GuestPage.vue'),
    meta: { 
      title: '访客专区', 
      auth: true,
      isDyn:true,
      roles: ['guest'] // 仅 guest 角色可访问
    }
  }
]

静态路由

路径:@/router/utils/static_router.js

// 静态路由表
export const static_router = [
  {
    path: '/login',
    name: 'Login',
    component: () => import('@/views/Login.vue'),
    meta: { title: '登录', auth: false } // 不需要权限
  },
  {
    path: '/403',
    name: 'Forbidden',
    component: () => import('@/views/403.vue'),
    meta: { title: '403', auth: false }
  },
  {
    path: '/404',
    name: 'NotFound',
    component: () => import('@/views/404.vue'),
    meta: { title: '404', auth: false }
  },
  {
    path: '/',
    redirect: '/dashboard', // 登录后默认跳转仪表盘,但本身是公开路由(无 token 时会被守卫拦截)
    meta: { auth: false }
  },
];

白名单

路径:@/router/utils/white_list.js

//白名单
export const Whitelist = [
  '/login',
  '/403',
  '/404',
  '/register',        // 如果有注册页
  '/forgot-password', // 如果有忘记密码页
  '/',                 // 根路径(通常重定向到仪表盘,但无 token 时会被守卫拦截,建议也加入白名单,守卫中再根据具体逻辑处理)
];

重置路由函数

路径:@/router/utils/reset_router.js

//重置路由 删除所有的动态路由规则
export const resetRouter = (router)=>{
    const routes = router.getRoutes()
    routes.forEach((route)=>{
        if(route.meta.isDyn){
            router.removeRoute(route.name)
        }
    })
}

根据身份过滤动态路由

路径:@/router/utils/router_filter.js

//根据身份过滤动态路由
export function routerFilter(role,routes){
  if(role&&routes){
    routes = routes.filter((route)=>{
      if(route.children){
        route.children = routerFilter(role,route.children)
      }
      return route.meta.roles.includes(role)
    })
  }
  return routes
}

设置用户状态管理

路径:@/sotres/user.js

import { ref, computed } from 'vue'
import { defineStore } from 'pinia'

export const useUserInfo = defineStore('user', () => {
  const userInfo = ref(JSON.parse(localStorage.getItem("userInfo"))||{})
  const token = ref (localStorage.getItem("token")||"")
  const final_dyn_router = ref([])
  function setUserInfo(value){
    localStorage.setItem("userInfo",JSON.stringify(value))
    userInfo.value = value
  }
  function setToken(value){
    localStorage.setItem("token",value)
    token.value = value
  }
  function setFinalDYNRouter(value){
    final_dyn_router.value =value
  }
  function removeUserInfo(){
    localStorage.removeItem("userInfo")
    userInfo.value = {}
  }
  function removeToken(){
    localStorage.removeItem("token")
    token.value = ""
  }
  function removeFinalDYNRouter(){
    final_dyn_router.value = []
  }
  return {
    setUserInfo,
    setToken,
    setFinalDYNRouter,
    userInfo,
    token,
    final_dyn_router,
    removeUserInfo,
    removeToken,
    removeFinalDYNRouter
  }
})


根组件

路径:@/App.vue

<template>
  <div>
    <button @click="loginout">退出登录</button>
  </div>
  <router-view></router-view>
</template>

<script setup>
import { useRouter } from 'vue-router';
import { useUserInfo } from './stores/user';
import { resetRouter } from './router/utils/reset_router';
const userInfoStore = useUserInfo()
const router = useRouter()
function loginout(){
  resetRouter(router)
  userInfoStore.removeFinalDYNRouter()
  userInfoStore.removeToken()
  userInfoStore.removeUserInfo()
  router.replace({path:"/login"})
}
</script>

<style>

</style>