RuoYi-Vue3路由守卫机制:前端路由权限拦截全解析

RuoYi-Vue3路由守卫机制:前端路由权限拦截全解析

【免费下载链接】RuoYi-Vue3 :tada: (RuoYi)官方仓库 基于SpringBoot,Spring Security,JWT,Vue3 & Vite、Element Plus 的前后端分离权限管理系统 【免费下载链接】RuoYi-Vue3 项目地址: https://gitcode.com/GitHub_Trending/ruo/RuoYi-Vue3

引言:路由守卫在权限系统中的核心地位

在现代前后端分离架构中,前端路由权限控制已成为系统安全的第一道防线。RuoYi-Vue3作为基于Vue3 & Vite、Element Plus构建的企业级权限管理系统,其路由守卫机制实现了从登录验证、权限过滤到动态路由生成的完整链路。本文将深入剖析RuoYi-Vue3的路由守卫实现原理,通过源码级解析和流程图解,帮助开发者掌握前端权限拦截的设计思想与最佳实践。

读完本文你将掌握:

  • 路由守卫的三级拦截体系设计
  • 动态路由生成的完整流程
  • 基于RBAC模型的权限校验实现
  • 复杂场景下的路由异常处理策略

一、路由守卫核心架构解析

1.1 技术架构概览

RuoYi-Vue3的路由权限系统采用"前置守卫+权限中心+动态路由"的三层架构,通过以下核心文件实现:

核心文件功能定位技术要点
src/permission.js全局路由守卫beforeEach/afterEach钩子、Token验证、进度条控制
src/router/index.js路由配置中心常量路由/动态路由分离、路由元信息定义
src/store/modules/permission.js权限状态管理后端路由拉取、路由过滤与格式化
src/utils/permission.js权限校验工具角色/权限字符串校验、组件级权限控制

1.2 路由守卫工作流程图

mermaid

二、全局路由守卫实现细节

2.1 前置守卫核心逻辑(beforeEach)

src/permission.js中的全局前置守卫实现了路由拦截的核心逻辑,其代码结构采用"分层过滤"设计:

router.beforeEach((to, from, next) => {
  NProgress.start() // 启动进度条
  if (getToken()) {
    // 已登录状态处理
    if (to.path === '/login') {
      next({ path: '/' }) // 已登录跳转首页
    } else if (isWhiteList(to.path)) {
      next() // 白名单路由直接放行
    } else {
      // 权限加载判断
      if (useUserStore().roles.length === 0) {
        // 首次加载用户信息与权限
        useUserStore().getInfo().then(() => {
          usePermissionStore().generateRoutes().then(accessRoutes => {
            accessRoutes.forEach(route => {
              router.addRoute(route) // 动态添加路由
            })
            next({ ...to, replace: true }) // hack确保路由已加载
          })
        }).catch(err => {
          // 权限加载失败处理
          useUserStore().logOut().then(() => {
            ElMessage.error(err)
            next({ path: '/' })
          })
        })
      } else {
        next() // 权限已加载直接放行
      }
    }
  } else {
    // 未登录状态处理
    if (isWhiteList(to.path)) {
      next() // 白名单路由直接放行
    } else {
      next(`/login?redirect=${to.fullPath}`) // 重定向到登录页
    }
  }
})

2.2 关键技术点解析

2.2.1 白名单机制实现
const whiteList = ['/login', '/register']

const isWhiteList = (path) => {
  return whiteList.some(pattern => isPathMatch(pattern, path))
}

采用some()结合isPathMatch函数实现路由模糊匹配,支持通配符路由(如/login*),提升白名单配置灵活性。

2.2.2 动态路由添加策略
usePermissionStore().generateRoutes().then(accessRoutes => {
  accessRoutes.forEach(route => {
    if (!isHttp(route.path)) {
      router.addRoute(route) // 动态添加可访问路由表
    }
  })
  next({ ...to, replace: true }) // hack方法确保addRoutes已完成
})

通过router.addRoute逐个添加路由,使用replace: true避免路由历史记录问题,解决动态路由添加时的"路由不存在"错误。

三、动态路由生成系统

3.1 路由数据流向

RuoYi-Vue3采用"后端返回路由结构+前端动态渲染"的方案,路由数据流程如下:

mermaid

3.2 路由格式化核心代码

src/store/modules/permission.js中实现了路由数据的格式化与组件加载:

// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
  return asyncRouterMap.filter(route => {
    if (route.component) {
      // Layout ParentView 组件特殊处理
      if (route.component === 'Layout') {
        route.component = Layout
      } else if (route.component === 'ParentView') {
        route.component = ParentView
      } else if (route.component === 'InnerLink') {
        route.component = InnerLink
      } else {
        route.component = loadView(route.component) // 加载组件
      }
    }
    if (route.children && route.children.length) {
      route.children = filterAsyncRouter(route.children, route, type)
    } else {
      delete route['children']
      delete route['redirect']
    }
    return true
  })
}

// 动态加载组件
export const loadView = (view) => {
  let res
  for (const path in modules) {
    const dir = path.split('views/')[1].split('.vue')[0]
    if (dir === view) {
      res = () => modules[path]()
    }
  }
  return res
}

3.3 路由权限过滤机制

// 动态路由遍历,验证是否具备权限
export function filterDynamicRoutes(routes) {
  const res = []
  routes.forEach(route => {
    if (route.permissions) {
      if (auth.hasPermiOr(route.permissions)) {
        res.push(route)
      }
    } else if (route.roles) {
      if (auth.hasRoleOr(route.roles)) {
        res.push(route)
      }
    }
  })
  return res
}

通过auth.hasPermiOrauth.hasRoleOr实现基于权限字符串和角色的双重过滤,确保用户只能访问有权限的路由。

四、权限校验体系

4.1 权限校验工具函数

src/utils/permission.js提供了组件级权限校验能力:

/**
 * 字符权限校验
 * @param {Array} value 校验值
 * @returns {Boolean}
 */
export function checkPermi(value) {
  if (value && value instanceof Array && value.length > 0) {
    const permissions = useUserStore().permissions
    const permissionDatas = value
    const all_permission = "*:*:*"

    return permissions.some(permission => {
      return all_permission === permission || permissionDatas.includes(permission)
    })
  }
  return false
}

/**
 * 角色权限校验
 * @param {Array} value 校验值
 * @returns {Boolean}
 */
export function checkRole(value) {
  if (value && value instanceof Array && value.length > 0) {
    const roles = useUserStore().roles
    const permissionRoles = value
    const super_admin = "admin"

    return roles.some(role => {
      return super_admin === role || permissionRoles.includes(role)
    })
  }
  return false
}

4.2 权限粒度控制

RuoYi-Vue3实现了三级权限控制体系:

权限级别控制对象实现方式应用场景
路由级页面访问权限路由元信息permissions/roles限制页面访问
按钮级操作权限v-hasPermi指令限制按钮/操作可见性
数据级数据行权限后端接口过滤限制数据访问范围

五、路由异常处理策略

5.1 常见异常场景处理

5.1.1 Token失效处理
// src/utils/request.js
import { isRelogin } from '@/utils/request'

// 响应拦截器中处理Token失效
service.interceptors.response.use(
  response => { ... },
  error => {
    if (error.response && error.response.status === 401) {
      if (!isRelogin.show) {
        isRelogin.show = true
        useUserStore().logOut().then(() => {
          location.href = '/login'
        })
      }
      return Promise.reject(error)
    }
  }
)

通过isRelogin标志位防止重复登出,确保在Token失效时优雅跳转登录页。

5.1.2 路由不存在处理
// src/router/index.js
{
  path: "/:pathMatch(.*)*",
  component: () => import('@/views/error/404'),
  hidden: true
}

使用Vue Router的全匹配路由,将未匹配的路由统一重定向到404页面。

5.2 性能优化策略

5.2.1 路由懒加载实现
// 路由组件懒加载
const modules = import.meta.glob('./../../views/**/*.vue')

export const loadView = (view) => {
  let res
  for (const path in modules) {
    const dir = path.split('views/')[1].split('.vue')[0]
    if (dir === view) {
      res = () => modules[path]()
    }
  }
  return res
}

采用import.meta.glob实现路由组件的动态导入,配合Vite的代码分割功能,减少初始加载资源体积。

5.2.2 路由缓存策略
// src/layout/components/AppMain.vue
<template>
  <keep-alive :include="cachedViews">
    <router-view :key="key" />
  </keep-alive>
</template>

<script>
export default {
  computed: {
    cachedViews() {
      return useTagsViewStore().cachedViews
    },
    key() {
      return this.$route.fullPath
    }
  }
}
</script>

通过<keep-alive>结合标签页管理实现路由组件缓存,提升页面切换性能。

六、最佳实践与扩展建议

6.1 路由设计最佳实践

  1. 路由命名规范

    • 使用kebab-case命名路由path
    • 路由name与组件名保持一致
    • 路由元信息必须包含title和icon
  2. 动态路由优化

    • 后端返回路由结构时使用树形结构
    • 减少深层嵌套路由(建议不超过3层)
    • 合理使用redirect简化路由结构

6.2 扩展功能建议

  1. 细粒度权限控制

    • 实现基于ABAC模型的属性权限控制
    • 添加路由访问频率限制
  2. 路由审计日志

    • 在路由后置守卫中记录访问日志
    • 实现敏感操作审计追踪

七、总结与展望

RuoYi-Vue3的路由守卫机制通过精心设计的架构,实现了安全、灵活、高效的前端权限控制。其核心优势在于:

  1. 完整的权限生态:从路由到组件的全链路权限控制
  2. 灵活的动态路由:支持后端驱动的路由配置
  3. 优雅的异常处理:完善的边界情况处理策略
  4. 优秀的性能表现:组件懒加载与缓存机制

随着前端安全需求的不断提升,路由守卫机制将向更细粒度的权限控制、更智能的异常检测方向发展。建议开发者在实际项目中,结合业务场景灵活调整权限策略,构建既安全又易用的权限系统。

附录:核心API参考

API功能描述参数说明
router.beforeEach全局前置守卫(to, from, next) => {}
router.addRoute添加动态路由(routeConfig)
usePermissionStore().generateRoutes生成动态路由() => Promise<RouteRecordRaw[]>
checkPermi权限字符串校验(permissions: string[]) => boolean
checkRole角色校验(roles: string[]) => boolean

【免费下载链接】RuoYi-Vue3 :tada: (RuoYi)官方仓库 基于SpringBoot,Spring Security,JWT,Vue3 & Vite、Element Plus 的前后端分离权限管理系统 【免费下载链接】RuoYi-Vue3 项目地址: https://gitcode.com/GitHub_Trending/ruo/RuoYi-Vue3

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值