思路是对的,`meta` 标记 + 全局前置守卫就是标准解法,但第二步那个守卫直接读 `store.user.role` 有个高频坑:刷新页面时 store 是空的、用户信息还在异步接口里没回来,而守卫是同步跑完的——结果要么 `store.user` 报 undefined,要么把已经登录的人踢去 403。

改法是把守卫写成 `async`:有 token 但 `store.user` 为空时,先 `await` 拉一次用户信息再继续判断;拉取失败(401/过期)就清掉 token 再跳登录。顺便把「有没有 token」升级成「token 还有效」,因为 localStorage 里的过期 token 依然存在,光判断存在会放行一堆废请求。

另外标题里的「动态路由」正文还没展开,补一下:真要按角色注入路由,得在拉完用户信息后调 `router.addRoute()`。这里最经典的坑是兜底路由的顺序——如果静态表里已经注册了 `/:pathMatch(.)`,后面动态加的子路由会一直被 404 吃掉。做法是先给兜底路由起个 `name`,动态路由加完后再 `addRoute` 一次同名路由覆盖它(Vue Router 4 同名会自动替换)。

最后提醒一个安全点:`query.redirect` 是 URL 里带进来的,登录后直接 `router.push(redirect)` 前判断一下是否以单个 `/` 开头,否则就是开放重定向,容易被拿去做钓鱼跳板。