Vue项目中根据角色动态加载路由权限时为什么某些菜单还是能访问?
我在做权限控制时,根据后端返回的角色动态过滤路由,但测试发现没有对应权限的角色依然能通过URL直接访问页面。比如普通用户本该看不到的「系统管理」菜单,输入路径后居然能跳转。
尝试过在路由配置里给每个路由加了meta: { roles: ['admin'] },然后在导航守卫里写:
router.beforeEach((to, from, next) => {
const userRoles = store.state.roles;
if (to.meta.roles && !to.meta.roles.includes(userRoles[0])) {
next('/403');
} else {
next();
}
});
但问题来了:用户的角色是数组格式(比如['editor', 'viewer']),而判断逻辑只能匹配单个角色。改用数组includes方法后,控制台又报错说userRoles[0] is undefined,难道是状态没传对吗?
先说第一个问题。你的 meta.roles 是个数组,比如 ['admin'],而用户角色也是数组格式,比如 ['editor', 'viewer']。你现在用的是 to.meta.roles.includes(userRoles[0]),意思就是拿用户的第一个角色去查是否在目标路由的角色白名单里。但这样只能匹配 admin 用户能访问的页面,其他角色全被挡在外面了,而且一旦 userRoles 还没拿到(比如刚进入页面时 store 为空),就会报 userRoles[0] is undefined。
正确的做法应该是:判断用户角色数组和路由要求的角色有没有交集。你可以这么改:
第二个问题是时机问题。很多人的角色信息是从服务端异步拉的,比如登录后发个请求 getProfile 拿到 roles 写进 store。但这个时候路由守卫可能已经执行了,store 里 roles 还是空的,导致所有权限判断都失败。
解决方案是在应用启动时确保角色加载完成,或者加个前置判断:如果还没登录或角色没加载,先跳登录页或者做个初始化路由,等数据回来再放行。
建议你在 app 初始化的时候就从服务端把用户权限拉下来,不要等到路由守卫里才去读 state。可以搞个 initAuth() 的方法,在 main.js 里先调用一次,resolve 之后再挂载 Vue 实例,避免状态不同步。
还有提醒一点:前端路由控制只是体验层面的限制,真正敏感接口和服务端资源一定要在服务端也做角色校验,别信前端那层遮羞布。
改一下就行。先确保你的用户角色数据已经正确存进 Vuex 或 pinia 里了,别在守卫执行时还是空的。然后导航守卫这么写:
另外记得后端接口返回的角色名要和前端 meta.roles 里定义的一致,别一个叫 admin,一个叫 superuser 对不上。
还有个小建议:动态路由最好是在登录后根据角色生成可访问的路由表,用 router.addRoute 动态挂载,而不是全量注册后再靠守卫拦。这样更安全,连路由都注册了,就算跳转被拦住,代码里还是能看见页面组件。