坦途的博客

vuePress-theme-reco 坦途    2020 - 2025
坦途的博客 坦途的博客
主页
博客
  • 开发实践
  • 技术笔记
  • 技术实践
  • 前端优化
  • 鉴权
  • 心情物语
  • 杂谈游记
  • 前端
标签
时间轴
关于
author-avatar

坦途

22

文章

33

标签

主页
博客
  • 开发实践
  • 技术笔记
  • 技术实践
  • 前端优化
  • 鉴权
  • 心情物语
  • 杂谈游记
  • 前端
标签
时间轴
关于

基于 Vue 3 + JWT 实现动态路由与用户权限控制

vuePress-theme-reco 坦途    2020 - 2025

基于 Vue 3 + JWT 实现动态路由与用户权限控制

坦途 2025-01-02 vuetoken

# 前端权限管理最佳实践:基于 Vue 3 + JWT 实现动态路由与用户权限控制

在现代前端项目中,权限管理是一个至关重要的功能,特别是在后台管理系统中,我们需要确保不同角色的用户只能访问相应的页面和功能。本文将基于 Vue 3 + Vue Router + Pinia + JWT,实现一个完整的 前端权限管理方案,包括 登录鉴权、动态路由加载、按钮级权限控制 以及 Token 续期 机制。


# 一、权限管理的核心概念

# 1. JWT 认证流程

前端权限管理通常依赖后端的身份验证,而 JWT(JSON Web Token)是一种常见的身份认证方式。它的基本流程如下:

  1. 用户登录 -> 账号密码提交给后端
  2. 后端验证身份 -> 验证成功后生成 JWT 令牌(Token)
  3. 前端存储 Token -> 将 Token 存入 localStorage 或 sessionStorage
  4. 请求携带 Token -> 访问接口时在 Authorization 头部附带 Token
  5. 后端验证 Token -> 确保请求有效并返回数据
  6. Token 续期 -> 过期时通过 refreshToken 续期

# 二、项目初始化

我们使用 Vue 3 进行开发,并引入 Vue Router、Pinia 进行状态管理,同时 Axios 作为 HTTP 请求库。

# 1. 安装依赖

npm install vue-router@4 pinia axios

# 2. 配置 Vue Router

创建 router/index.js,定义基础的静态路由和动态权限路由。

import { createRouter, createWebHistory } from 'vue-router';
import { useAuthStore } from '@/stores/auth';
import Login from '@/views/Login.vue';
import NotFound from '@/views/NotFound.vue';

const routes = [
  { path: '/login', component: Login, meta: { requiresAuth: false } },
  { path: '/:pathMatch(.*)*', component: NotFound }
];

const router = createRouter({
  history: createWebHistory(),
  routes
});

// 路由守卫:检查用户是否有权限访问某个页面
router.beforeEach((to, from, next) => {
  const authStore = useAuthStore();
  if (to.meta.requiresAuth && !authStore.isAuthenticated) {
    next('/login');
  } else {
    next();
  }
});

export default router;

# 三、实现登录认证

在 store/auth.js 中使用 Pinia 进行 Token 管理。

import { defineStore } from 'pinia';
import axios from 'axios';

export const useAuthStore = defineStore('auth', {
  state: () => ({
    token: localStorage.getItem('token') || '',
    userInfo: JSON.parse(localStorage.getItem('userInfo')) || null
  }),
  getters: {
    isAuthenticated: state => !!state.token
  },
  actions: {
    async login(credentials) {
      try {
        const response = await axios.post('/api/login', credentials);
        this.token = response.data.token;
        this.userInfo = response.data.user;
        localStorage.setItem('token', this.token);
        localStorage.setItem('userInfo', JSON.stringify(this.userInfo));
      } catch (error) {
        console.error('登录失败', error);
      }
    },
    logout() {
      this.token = '';
      this.userInfo = null;
      localStorage.removeItem('token');
      localStorage.removeItem('userInfo');
    }
  }
});

在 Login.vue 组件中调用 login 方法:

<script setup>
import { ref } from 'vue';
import { useAuthStore } from '@/stores/auth';
import { useRouter } from 'vue-router';

const authStore = useAuthStore();
const router = useRouter();
const username = ref('');
const password = ref('');

const handleLogin = async () => {
  await authStore.login({ username: username.value, password: password.value });
  if (authStore.isAuthenticated) {
    router.push('/');
  }
};
</script>

<template>
  <div>
    <input v-model="username" placeholder="用户名" />
    <input v-model="password" type="password" placeholder="密码" />
    <button @click="handleLogin">登录</button>
  </div>
</template>

# 四、动态路由权限控制

不同角色的用户应该有不同的菜单和页面访问权限。比如:

  • admin 角色可以访问 用户管理、订单管理、系统设置
  • editor 角色只能访问 文章管理

# 1. 定义动态路由

在 router/permissionRoutes.js 里定义权限路由:

export const asyncRoutes = [
  {
    path: '/dashboard',
    component: () => import('@/views/Dashboard.vue'),
    meta: { requiresAuth: true, roles: ['admin', 'editor'] }
  },
  {
    path: '/user-management',
    component: () => import('@/views/UserManagement.vue'),
    meta: { requiresAuth: true, roles: ['admin'] }
  },
  {
    path: '/article',
    component: () => import('@/views/Article.vue'),
    meta: { requiresAuth: true, roles: ['editor'] }
  }
];

# 2. 角色鉴权

在 router/index.js 里动态加载路由:

import { asyncRoutes } from './permissionRoutes';
import { useAuthStore } from '@/stores/auth';

router.beforeEach((to, from, next) => {
  const authStore = useAuthStore();
  if (authStore.isAuthenticated) {
    // 根据角色动态添加路由
    const allowedRoutes = asyncRoutes.filter(route =>
      route.meta.roles.includes(authStore.userInfo.role)
    );
    allowedRoutes.forEach(route => router.addRoute(route));
  }
  next();
});

# 五、按钮级权限控制

在 Vue 组件中,我们可以使用 v-if 来控制按钮显示:

<template>
  <button v-if="hasPermission('edit')">编辑</button>
  <button v-if="hasPermission('delete')">删除</button>
</template>

<script setup>
import { useAuthStore } from '@/stores/auth';

const authStore = useAuthStore();
const hasPermission = (action) => {
  return authStore.userInfo.permissions.includes(action);
};
</script>

# 六、Token 续期

JWT 可能会过期,因此我们需要实现 Token 续期:

axios.interceptors.response.use(
  response => response,
  async error => {
    if (error.response.status === 401) {
      const authStore = useAuthStore();
      const refreshResponse = await axios.post('/api/refresh-token', { token: authStore.token });
      authStore.token = refreshResponse.data.token;
      localStorage.setItem('token', authStore.token);
      return axios(error.config); // 重新发送失败的请求
    }
    return Promise.reject(error);
  }
);