在 Vue 中使用 JWT、Directives & Route Guards 实现基于权限的访问控制
In modern web applications, controlling what each user can see and do is crucial for both security and usability. Whether you’re building a SaaS dashboard or an internal tool, role-based and permission-based access control is a must-have feature.
在现代 Web 应用程序中,控制每个用户可以看到和做什么对于安全性和可用性至关重要。无论您是在构建 SaaS 仪表板还是内部工具,基于角色和基于权限的访问控制 都是必备功能。
In this blog, I’ll walk you through how I implemented permission-based access control in a Vue 3 application using:
在本博客中,我将带你一步步了解我是如何在Vue 3 应用中使用以下方式实现基于权限的访问控制的:
- JWT tokens from the backend
- 来自后端的 JWT tokens
- Custom directives for disabling UI
- 自定义指令,用于禁用 UI
- Route guards for blocking unauthorized navigation
- 路由守卫,用于阻止未授权导航
This setup provides robust control without cluttering your components.
这种设置提供了强大的控制,而不会使您的组件杂乱。
The Goal
目标
We want to:
我们想要:
✅ Decode user permissions from a JWT token
✅ Enable/disable UI actions based on those permissions
✅ Show tooltips when features are disabled
✅ Hide restricted features entirely
✅ Block unauthorised routes via router guards
✅ 从 JWT token 中解码用户权限
✅ 根据这些权限启用/禁用 UI 操作
✅ 当功能被禁用时显示工具提示
✅ 完全隐藏受限功能
✅ 通过 router guards 阻止未授权路由
Permissions Design
权限设计
The backend sends a JWT token with a permissions object embedded:
后端发送一个带有嵌入 permissions 对象的 JWT 令牌:
{
"permissions": {
"dashboardAnalytics": "full",
"projectSettings": "readonly",
"userManagement": "denied"
}
}
{
"permissions": {
"dashboardAnalytics": "full",
"projectSettings": "readonly",
"userManagement": "denied"
}
}
Each permission can be:
每个权限可以是:
full– full accessfull– 完全访问readonly– can view, not modifyreadonly– 可以查看,不能修改denied– cannot access at alldenied– 完全无法访问
1. Decoding JWT & Managing Permissions
1. 解码 JWT & 管理权限
Store the permissions in a reactive ref:
将权限存储在响应式的 ref 中:
// @utils/permission.ts
import { ref } from 'vue'
// @utils/permission.ts
import { ref } from 'vue'
export const permissionList = ref<Record<string, string>>({})
export const getAllPermissions = (() => {
const apiToken = document.cookie
.split('; ')
.find(row => row.startsWith('api_token='))
?.split('=')[1]
if (apiToken) {
const ...