You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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")
    }
})

问题根因

三个问题共同导致跳转失效:

  1. 中间件参数签名完全错误:defineNuxtRouteMiddleware的固定参数顺序为(to, from),仅接收两个参数,不存在第三个参数。你错误地将第一个参数(真正的to目标路由对象)当成Nuxt上下文解构$pinia,导致代码中引用的to变量实际是来源路由from,也就是你触发跳转前所在的dashboard页面,这就是打印to始终显示/dashboard/owner的直接原因。Nuxt3中useAuthStore、useCookie等composable可直接在中间件内调用,不需要从上下文解构$pinia。
  2. 重定向逻辑死循环:你将清空登录态的逻辑放在登录页中间件中,意味着必须成功跳转至登录页才会清除token,但Panel面板中间件会在所有路由跳转时执行——当你发起跳转到登录页的请求时,token还未被清除,面板中间件检测到token存在,会直接将请求重定向回对应权限的dashboard页面,导致永远到不了登录页,形成死循环。
  3. 缺少公共路由白名单:面板中间件没有排除登录页等不需要权限的公共路由,所有跳转都会被权限校验逻辑拦截。

修复方案

按以下步骤修改即可解决问题:

  1. 调整登出逻辑,不要依赖路由中间件清除登录态,触发登出时直接清除状态后再跳转:
// 登出事件处理逻辑
const handleLogout = () => {
  const token = useCookie('accessToken')
  const authStore = useAuthStore()
  // 先清状态再跳转
  token.value = null
  authStore.$reset()
  return navigateTo('/login?logout=true', { replace: true })
}
  1. 给面板中间件添加公共路由白名单,公共路由直接放行不做权限校验,同时修正参数签名:
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")
    }
})
  1. 修正登录页中间件的参数签名,用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 06:24:24