@[toc]
在 Vue 项目里,权限问题永远不是"有没有",而是"会不会失控"。
一开始可能只是:
- 登录校验
- 菜单控制
- 页面访问限制
但随着业务复杂度上来,很容易演变成:
- 路由守卫越写越多
- 页面里到处是 if (hasPermission)
- 前后端权限逻辑不一致
这篇文章,我们不讲"能跑的权限",而是讲可扩展、可维护、长期稳定的权限系统设计。
一、先明确:权限系统到底在管什么?
先给一个非常重要的拆分:
权限 = 三件事
- 能不能进这个路由
- 能不能看到这个页面的入口
- 能不能执行某个操作(按钮级)
这篇文章重点讲 第 1 件事:路由权限。
二、权限设计的核心原则
在任何中大型项目里,下面三条原则一定要守住。
原则一:权限是"声明式"的
路由只声明自己需要什么权限,而不是怎么判断。
ts
meta: {
requiresAuth: true,
roles: ['admin']
}
原则二:权限判断逻辑集中
绝不分散在页面、组件、API 调用里。
原则三:路由权限 ≠ 菜单权限
- 路由权限:是否允许访问 URL
- 菜单权限:是否展示入口
两者相关,但不要强耦合。
三、基础路由权限模型
路由声明
ts
{
path: '/admin',
component: () => import('@/views/admin/index.vue'),
meta: {
requiresAuth: true,
roles: ['admin']
}
}
全局守卫
ts
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !isLogin()) {
return next('/login')
}
next()
})
这是最基础的一层,但还远远不够。
四、角色权限系统的正确写法
权限判断函数集中化
ts
function hasPermission(routeRoles: string[]) {
const userRoles = getUserRoles()
return routeRoles.some(role => userRoles.includes(role))
}
权限守卫
ts
router.beforeEach((to, from, next) => {
const { roles } = to.meta
if (roles && !hasPermission(roles)) {
return next('/403')
}
next()
})
好处:
- 权限逻辑只有一个入口
- 改规则不用全局搜代码
五、动态路由与后端权限
中后台项目几乎都会遇到:
路由由后端返回,前端动态注册
后端返回示例
json
[
{
"path": "/order",
"component": "order/index",
"roles": ["admin"]
}
]
前端动态注册
ts
const asyncRoutes = mapBackendRoutes(routesFromServer)
asyncRoutes.forEach(route => {
router.addRoute(route)
})
关键点
- 前端仍然保留权限校验
- 后端负责"能看到什么"
- 前端负责"能不能访问"
六、权限与页面逻辑解耦
错误示例:
vue
<button v-if="user.role === 'admin'">删除</button>
正确做法:
ts
const canDelete = usePermission('delete_order')
统一用权限 Hook / 方法,避免散落逻辑。
七、实战总结
一个稳定的 Vue Router 权限系统应该做到:
- 路由声明权限
- 守卫集中判断
- 页面不感知权限逻辑
- 支持后端动态配置