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

基于Pinia与Vue Router实现Token刷新及路由鉴权求助

Vue路由保护实现优化

先解决核心问题:异步请求的异常处理与边界情况

你的代码已经有了基础的路由守卫逻辑,但缺少对token不存在、请求失败、业务状态判断这些场景的处理,接下来一步步优化:


1. 拦截无Token的情况,避免无效请求

在调用校验接口前,先检查LocalStorage里有没有token,没有直接判定未登录,同时处理请求异常,防止路由卡住:

router.beforeEach(async (to, from, next) => {
    const authStore = useAuthStore();
    const token = localStorage.getItem('token');
    let isLogged = false;

    if (token) {
        try {
            // 只在有token时发起校验请求
            isLogged = await authStore.refresh(token);
        } catch (err) {
            console.error('Token校验请求出错:', err);
        }
    }

    if (to.meta.authRequired) {
        if (isLogged) {
            next();
        } else {
            // 清除无效token,避免下次重复校验
            localStorage.removeItem('token');
            next("/login");
        }
    } else {
        // 已登录用户访问登录页,自动跳转到首页
        if (isLogged && to.path === '/login') {
            next('/');
        } else {
            next();
        }
    }
})

2. 完善Token校验的业务逻辑

response.ok只代表HTTP状态码是2xx,但后端可能返回200但业务上token已失效,必须解析响应体判断:

async refresh(token) {
    try {
        const response = await fetch("XXXXXXXXXX", {
            method: "GET",
            headers: {
                "Content-type": "application/x-www-form-urlencoded",
                "Token": token // 直接传token,不需要模板字符串
            }
        })

        if (!response.ok) {
            throw new Error('请求异常');
        }

        // 解析后端返回的JSON数据,根据实际字段判断有效性
        const resData = await response.json();
        // 假设后端返回{ valid: true }表示token有效
        return resData.valid === true;
    } catch (err) {
        console.error('Token校验失败:', err);
        return false;
    }
}

3. 避免重复发起校验请求

快速切换路由时会多次调用refresh,可以在Pinia里加个请求锁,防止重复请求:

// 在useAuthStore中添加状态
const isRefreshing = ref(false);
const isLogged = ref(false);

async refresh(token) {
    // 如果正在校验,等待请求结束再返回结果
    if (isRefreshing.value) {
        await new Promise(resolve => {
            const checkInterval = setInterval(() => {
                if (!isRefreshing.value) {
                    clearInterval(checkInterval);
                    resolve();
                }
            }, 50);
        });
        return isLogged.value;
    }

    isRefreshing.value = true;
    try {
        const response = await fetch("XXXXXXXXXX", {
            method: "GET",
            headers: {
                "Content-type": "application/x-www-form-urlencoded",
                "Token": token
            }
        })

        if (!response.ok) throw new Error('请求失败');
        const resData = await response.json();
        isLogged.value = resData.valid === true;
        return isLogged.value;
    } catch (err) {
        console.error('Token校验出错:', err);
        isLogged.value = false;
        return false;
    } finally {
        isRefreshing.value = false;
    }
}

对应的路由守卫可以简化,直接用store里的状态:

router.beforeEach(async (to, from, next) => {
    const authStore = useAuthStore();
    const token = localStorage.getItem('token');

    // 只有有token、未登录且未在校验时,才发起请求
    if (token && !authStore.isLogged && !authStore.isRefreshing) {
        await authStore.refresh(token);
    }

    if (to.meta.authRequired) {
        authStore.isLogged ? next() : next("/login");
    } else {
        // 已登录用户访问登录页自动跳转首页
        authStore.isLogged && to.path === '/login' ? next('/') : next();
    }
})

内容的提问来源于stack exchange,提问作者Will Galvão

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 00:20:16