Nuxt3调用router.replace跳转登录页失效问题排查
Nuxt3 路由中间件权限控制登出跳转失效问题
实现逻辑说明
项目配置了两个路由中间件做权限控制:
- 登录页中间件:已登录用户访问登录页时,自动重定向到自身最高角色权限对应的dashboard页面,例如同时拥有user、owner权限则跳转
/owner路径,仅拥有user权限则跳转/user路径 - Panel面板中间件:校验访问用户的角色权限,无目标页面访问权限时重定向回登录页,例如worker角色尝试访问owner仪表盘时,会被重定向至登录页
登出逻辑设计:在登录页中间件添加query参数判断,访问路径携带logout=true参数(即login?logout=true)时,清空登录态后跳转回登录页。预期效果为用户触发登出操作时,dashboard页面将用户重定向到带该query参数的登录页,但实际跳转始终未生效。
异常表现
执行重定向逻辑时,登录页中间件内的console.log(to)打印的目标路径fullPath始终为/dashboard/owner,从未进入预期的登录页路由。
问题相关代码
登出重定向方法
this.$router.replace({ name: 'login', query: { 'logout': true } })
登录页中间件(第一个中间件)
import { authoritiesRoutes } from "~~/middleware/panel" import { useAuthStore } from "~~/store/auth" export default defineNuxtRouteMiddleware(async ({ $pinia }, to, from) => { const token = useCookie('accessToken') console.log(to) if (to.query.logout === 'true') { token.value = null return navigateTo('/login') } if (token.value) { const store = useAuthStore($pinia) await store.retrieveUser(token.value) if (store.user != null) { const permissionLevel = store.getUser.authorities.length const routeName = authoritiesRoutes[permissionLevel] const route = '/' + routeName.replace('-', '/') return route; } } })
Panel面板中间件(第二个中间件)
import { useAuthStore } from '~~/store/auth' export enum authoritiesRoutes { 'panel' = 1, 'dashboard' = 2, 'dashboard-owner' = 3 } export default defineNuxtRouteMiddleware(async ({ $pinia, $event }, to) => { const token = useCookie('accessToken') if (!token.value) { return navigateTo('/login') } const store = useAuthStore($pinia) await store.retrieveUser(token.value) if (store.user == null) { return navigateTo('/login') } const permissionLevel = authoritiesRoutes[to.name]; if (store.getUser.authorities.length < permissionLevel) { return navigateTo("/login?error=you%20don't%20have%20permission%20to%20watch%20this%20page") } })
问题根因
三个问题共同导致跳转失效:
- 中间件参数签名完全错误:
defineNuxtRouteMiddleware的固定参数顺序为(to, from),仅接收两个参数,不存在第三个参数。你错误地将第一个参数(真正的to目标路由对象)当成Nuxt上下文解构$pinia,导致代码中引用的to变量实际是来源路由from,也就是你触发跳转前所在的dashboard页面,这就是打印to始终显示/dashboard/owner的直接原因。Nuxt3中useAuthStore、useCookie等composable可直接在中间件内调用,不需要从上下文解构$pinia。 - 重定向逻辑死循环:你将清空登录态的逻辑放在登录页中间件中,意味着必须成功跳转至登录页才会清除token,但Panel面板中间件会在所有路由跳转时执行——当你发起跳转到登录页的请求时,token还未被清除,面板中间件检测到token存在,会直接将请求重定向回对应权限的dashboard页面,导致永远到不了登录页,形成死循环。
- 缺少公共路由白名单:面板中间件没有排除登录页等不需要权限的公共路由,所有跳转都会被权限校验逻辑拦截。
修复方案
按以下步骤修改即可解决问题:
- 调整登出逻辑,不要依赖路由中间件清除登录态,触发登出时直接清除状态后再跳转:
// 登出事件处理逻辑 const handleLogout = () => { const token = useCookie('accessToken') const authStore = useAuthStore() // 先清状态再跳转 token.value = null authStore.$reset() return navigateTo('/login?logout=true', { replace: true }) }
- 给面板中间件添加公共路由白名单,公共路由直接放行不做权限校验,同时修正参数签名:
import { useAuthStore } from '~~/store/auth' export enum authoritiesRoutes { 'panel' = 1, 'dashboard' = 2, 'dashboard-owner' = 3 } // 公共路由白名单,不需要登录就能访问 const publicRoutes = ['login'] export default defineNuxtRouteMiddleware(async (to) => { // 公共路由直接放行 if (publicRoutes.includes(to.name as string)) return const token = useCookie('accessToken') if (!token.value) { return navigateTo('/login') } const store = useAuthStore() await store.retrieveUser(token.value) if (store.user == null) { return navigateTo('/login') } const permissionLevel = authoritiesRoutes[to.name]; if (store.getUser.authorities.length < permissionLevel) { return navigateTo("/login?error=you%20don't%20have%20permission%20to%20watch%20this%20page") } })
- 修正登录页中间件的参数签名,用
navigateTo做重定向,添加重复重定向判断避免死循环:
import { authoritiesRoutes } from "~~/middleware/panel" import { useAuthStore } from "~~/store/auth" export default defineNuxtRouteMiddleware(async (to) => { const token = useCookie('accessToken') const store = useAuthStore() // 访问登录页的逻辑 if (to.path === '/login') { // logout参数兜底清状态 if (to.query.logout === 'true') { token.value = null store.$reset() } // 无token直接留在登录页,不重定向 if (!token.value) return } if (token.value) { // 如果store里没用户信息再拉取,避免重复请求 if (!store.user) { await store.retrieveUser(token.value) } if (store.user != null) { const permissionLevel = store.getUser.authorities.length const routeName = authoritiesRoutes[permissionLevel] const targetRoute = '/' + routeName.replace('-', '/') // 已经在目标路由就不重复重定向 if (to.path === targetRoute) return return navigateTo(targetRoute, { replace: true }) } } })
内容的提问来源于stack exchange,提问作者Georglider
相关产品推荐
相关产品推荐

