Vue Router路由导航守卫

Vue Router路由导航守卫

_

🎯 什么是路由导航守卫?

路由导航守卫是Vue Router提供的控制路由跳转的机制,允许我们在路由跳转前或跳转后执行一些逻辑。最常见的应用场景就是身份验证和权限控制。

🚪 导航守卫类型

Vue Router提供了三种守卫:

  1. 全局前置守卫router.beforeEach()

  2. 全局后置守卫router.afterEach()

  3. 路由独享守卫:在路由配置中定义

🔐 全局前置守卫示例

基础身份验证实现

import { createRouter, createWebHashHistory } from "vue-router";

// 导入页面组件
import Login from "../components/Login.vue";
import Table from "../components/table.vue";
import Menu from "../components/menu.vue";
import View from "../views/view.vue";
import Upload from "../components/menu_content/Upload.vue";
import Search from "../components/menu_content/Search.vue";
import ArrangeRoom from "../components/menu_content/arrange_room.vue";

// 定义路由配置
const routes = [
  {
    path: '/login',
    name: 'login',
    component: Login,
    meta: { requiresAuth: false }
  },
  {
    path: '/',
    name: 'home',
    component: View,
    meta: { requiresAuth: true }
  },
  {
    path: '/table',
    name: 'table',
    component: Table,
    meta: { requiresAuth: true }
  },
  {
    path: '/menu',
    name: 'menu', 
    component: Menu,
    meta: { requiresAuth: true }
  },
  {
    path: '/upload',
    name: 'upload',
    component: Upload,
    meta: { requiresAuth: true }
  },
  {
    path: '/search',
    name: 'search',
    component: Search,
    meta: { requiresAuth: true }
  },
  {
    path: '/arrange-room',
    name: 'arrangeRoom',
    component: ArrangeRoom,
    meta: { requiresAuth: true }
  }
];

// 创建路由实例
const router = createRouter({
  history: createWebHashHistory(),
  routes,
});

// 全局前置守卫
router.beforeEach((to, from, next) => {
  const token = localStorage.getItem("token");
  
  // 检查路由是否需要身份验证
  const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
  
  // 如果需要验证且没有token,跳转到登录页
  if (requiresAuth && !token) {
    next({ name: 'login' });
    console.log("导航守卫失败,跳到登录页");
    return;
  }
  
  // 如果已经登录且访问登录页,可以重定向到首页
  if (token && to.name === 'login') {
    next({ name: 'home' });
    console.log("已登录用户访问登录页,重定向到首页");
    return;
  }
  
  // 其他情况允许继续导航
  next();
  console.log("导航守卫成功,继续");
});

export default router;

🔄 完整的导航守卫实现

结合token验证和权限管理

import { createRouter, createWebHashHistory } from "vue-router";

const router = createRouter({
  history: createWebHashHistory(),
  routes: [
    // 公开路由
    {
      path: '/login',
      name: 'login',
      component: () => import('../components/Login.vue'),
      meta: { 
        requiresAuth: false,
        title: '登录'
      }
    },
    {
      path: '/register',
      name: 'register', 
      component: () => import('../components/Register.vue'),
      meta: { requiresAuth: false }
    },
    
    // 需要验证的路由
    {
      path: '/dashboard',
      name: 'dashboard',
      component: () => import('../views/Dashboard.vue'),
      meta: { 
        requiresAuth: true,
        roles: ['admin', 'user']
      }
    },
    {
      path: '/admin',
      name: 'admin',
      component: () => import('../views/Admin.vue'),
      meta: { 
        requiresAuth: true,
        roles: ['admin'] // 只有管理员可以访问
      }
    }
  ],
});

// 前置守卫
router.beforeEach(async (to, from, next) => {
  // 设置页面标题
  if (to.meta.title) {
    document.title = to.meta.title;
  }

  const token = localStorage.getItem("token");
  const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
  
  // 1. 检查是否需要身份验证
  if (requiresAuth) {
    if (!token) {
      // 没有token,跳转登录
      next({
        name: 'login',
        query: { redirect: to.fullPath } // 保存目标路径
      });
      return;
    }
    
    // 2. 检查用户权限(如果需要)
    if (to.meta.roles) {
      const userRole = localStorage.getItem("userRole");
      
      if (!userRole || !to.meta.roles.includes(userRole)) {
        // 权限不足
        next({ name: 'unauthorized' }); // 跳转到无权限页面
        return;
      }
    }
  }

  // 3. 已登录用户访问登录页,重定向到首页
  if (token && to.name === 'login') {
    next({ name: 'dashboard' });
    return;
  }

  // 4. 允许导航
  next();
});

// 后置守卫
router.afterEach((to, from) => {
  // 页面跳转后的处理
  console.log(`从 ${from.path} 跳转到 ${to.path}`);
  
  // 可以在这里添加统计代码、进度条结束等
  // hideProgress();
  // trackPageView(to.path);
});

export default router;

import

🛡️ 导航守卫与拦截器的配合

双重验证机制

// router.js - 路由守卫处理基础验证
router.beforeEach((to, from, next) => {
  const token = localStorage.getItem("token");
  
  if (to.name !== "login" && !token) {
    next({ name: "login" });
    return;
  }
  
  next();
});

// request.js - 请求拦截器处理详细验证
import axios from "axios";

const request = axios.create();

request.interceptors.request.use(config => {
  const token = localStorage.getItem("token");
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

request.interceptors.response.use(
  response => response,
  error => {
    if (error.response?.status === 401) {
      // token无效,清除并跳转登录
      localStorage.removeItem("token");
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);

💡 实用技巧和最佳实践

1. 动态添加路由

// 根据用户权限动态添加路由
function addDynamicRoutes(userRole) {
  if (userRole === 'admin') {
    router.addRoute({
      path: '/admin-panel',
      name: 'adminPanel',
      component: () => import('../views/AdminPanel.vue')
    });
  }
}

2. 路由进度条

import NProgress from 'nprogress';
import 'nprogress/nprogress.css';

router.beforeEach(() => {
  NProgress.start();
});

router.afterEach(() => {
  NProgress.done();
});

3. 路由缓存控制

router.beforeEach((to, from, next) => {
  // 处理页面缓存
  if (to.meta.keepAlive) {
    to.meta.$keepAlive = true;
  }
  next();
});

⚠️ 注意事项

  1. 避免死循环:确保守卫逻辑不会导致无限重定向

  2. 异步处理:守卫中如果需要异步操作,要正确处理Promise

  3. 权限更新:用户权限变更时要重新验证路由

  4. 错误处理:合理处理守卫中的异常情况

📝 总结

路由导航守卫是Vue应用安全的重要组成部分,它与axios拦截器配合使用:

  • 🚪 路由守卫:控制页面访问权限(前端验证)

  • 🛡️ 请求拦截器:验证API调用权限(后端验证)

  • 🔄 响应拦截器:处理token过期等异常情况

Vue 3 Pinia状态管理 2025-01-18
Axios token的获取 2024-07-04

评论区