基于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
相关产品推荐
相关产品推荐

