# 前端权限管理最佳实践:基于 Vue 3 + JWT 实现动态路由与用户权限控制
在现代前端项目中,权限管理是一个至关重要的功能,特别是在后台管理系统中,我们需要确保不同角色的用户只能访问相应的页面和功能。本文将基于 Vue 3 + Vue Router + Pinia + JWT,实现一个完整的 前端权限管理方案,包括 登录鉴权、动态路由加载、按钮级权限控制 以及 Token 续期 机制。
# 一、权限管理的核心概念
# 1. JWT 认证流程
前端权限管理通常依赖后端的身份验证,而 JWT(JSON Web Token)是一种常见的身份认证方式。它的基本流程如下:
- 用户登录 -> 账号密码提交给后端
- 后端验证身份 -> 验证成功后生成 JWT 令牌(Token)
- 前端存储 Token -> 将 Token 存入
localStorage或sessionStorage - 请求携带 Token -> 访问接口时在
Authorization头部附带 Token - 后端验证 Token -> 确保请求有效并返回数据
- 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);
}
);