关于解决token过期失效问题

本文详细介绍了一种基于Token的免登陆机制实现方案,通过Vue、Vuex、localStorage及axios等技术,实现了用户对Token的无感知操作,有效解决了Token过期失效的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、先认识下token

在这里插入图片描述

二、整体思路

在这里插入图片描述

三、实现步骤
1.理清各个文件作用

在这里插入图片描述

2.路由导航守卫

设置用户有无token访问主页,并且登录成功回到目标页

import Vue from 'vue'
import VueRouter from 'vue-router'
import store from '@/store/index.js'
Vue.use(VueRouter)
const router = new VueRouter({
  routes
})
// 路由导航守卫
router.beforeEach((to, from, next) => {
  if (to.path.startsWith('/user')) {
    // 判断token
    if (store.state.tokenInfo.token) {
      next()
    } else {
      next({
        path: '/login',
        query: {
          // 登录成功回到目标页
          backto: to.fullPath // fullPath 会拿到路由后面的查询字符串
        }
      })
    }
  } else {
    next()
  }
})
export default router
3.封装localStorage方法

目的在vuex中调用

/ 封装模块 使用localStorage实现持久化 只是进行保存
// 从localStorage中取出一项数据 名字叫name
export const getItem = name => {
  return JSON.parse(localStorage.getItem(name))
}
// 向localStorage中设置一项数据 名字为name里面设置值为obj
export const setItem = (name, obj) => {
  localStorage.setItem(name, JSON.stringify(obj))
}
// 删除
export const removeitem = name => {
  localStorage.removeItem(name)
}
4.vuex
import Vue from 'vue'
import Vuex from 'vuex'
import { setItem, getItem } from '@/utils/storage.js'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    // 保存公共数据
    tokenInfo: getItem('tokenInfo') || {}
  },
  mutations: {
    mSetTokenInfo (state, tokenObj) {
      state.tokenInfo = tokenObj
      // 因为刷新会丢失所以进行持久化 调用storage方法
      setItem('tokenInfo', tokenObj)
    }
  },
  // -------------------此次重点---------------------------------------------------------
  // 只跟新token 不跟新响应拦截器里面的refreshToken
  mUpdateToken (state, newToken) {
    state.tokenInfo.token = newToken
    setItem('tokenInfo', state.tokenInfo)
  },
  actions: {
  },
  modules: {
  }
})
5.封装axios 实现请求拦截器和响应拦截器(重点部分)

关于axios拦截器 可参考官方文档
(点我)axios拦截器官方跳转链接

/* 对axios进行二次封装
请求拦截器增加token
响应拦截器处理大数据
*/
import store from '@/store/index.js'
import axios from 'axios'
import JSONbig from 'json-bigint' // 引入大数字包
import router from '@/router/index.js'
/*
以前写法: axios.defaults.baseURL = 'XXX'
自定义写法:const xxx = axios.create({})
一个项目中可能有不同的基地址 就要用自定义写法设置不同的基地址
*/
const instance = axios.create({
  baseURL: 'http://ttapi.research.itcast.cn',
  transformResponse: [function (data) {
    if (data === '') {
      return false
    }
    try {
      // 如果没有遇到错误,就返回 JSONbig处理之后的数据
      return JSONbig.parse(data)
    } catch (err) {
      console.log('JSONbig转换出错', err)
      return data
    }
  }]
})
// 在instance上添加请求拦截器 补充请求头token信息
instance.interceptors.request.use(function (config) {
  // 从vuex中取出token
  const token = store.state.tokenInfo.token
  // 如果有token则 添加到headers中
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
}, function (error) {
  return Promise.reject(error)
})
//------------------------这里处理token过期--------------------------------------------
// 添加响应拦截器  处理401 token过期
instance.interceptors.response.use(function (response) {
  return response
}, async function (error) {
  // 如果是401错误 则做以下错误
  if (error.response.status === 401) {
    // 从vuex中取出token
    const refreshToken = store.state.tokenInfo.token
    // 是否有refreshToken
    if (refreshToken) {
      // 用refresh_token 重发请求 再次取回一个有效期的
      try {
       // 注意这里重新发请求要用axios  不能用封装的instance  url地址是根据接口文档写的
        const { data: res } = await axios({
          method: 'PUT',
          url: 'http://ttapi.research.itcast.cn/app/v1_0/authorizations',
          headers: {
            Authorization: `Bearer ${refreshToken}`
          }
        })
        // 定义赋值新的token
        const newToken = res.data.token
        // 跟新vuex
        store.commit('mUpdateToken', newToken)
        // 再发请求
        return instance(error.config)
      } catch {  // 如果没有拿到新的token 
        // 回登录页
        router.push({
          path: '/login',
          query: {
            // currentRoute表示当前路由对象
            backto: router.currentRoute.fullPath
          }
        })
      }
    } else {   // 如果没有refreshToken
      router.push({
        path: '/login',
        query: {
          backto: router.currentRoute.fullPath
        }
      })
    }
  } else {  // 如果不是401错误
    return Promise.reject(error)
  }
})
export default instance
四、小结

在这里插入图片描述

<think>好的,我现在需要解决用户关于如何处理或刷新过期Token认证机制的问题。根据用户提供的三个引用内容,我需要先理解每个引用的重点,然后结合这些信息来构建一个清晰的解决方案。 首先,引用[1]提到在Vue后台中,当Token过期时,可以通过刷新请求来获取新的Token,然后使用新Token重新发起之前的请求。这里的关键点是使用一个订阅队列(refreshSubscribers)来暂存需要重新发起的请求,待新Token获取后再执行它们。这似乎是一种常见的处理方式,可以避免并发请求时多次刷新Token问题。 引用[2]指出,如果Refresh Token过期或被篡改,服务器会要求用户重新登录。这说明在设计中需要考虑Refresh Token的有效期和安全性,以及如何处理Refresh Token失效的情况,比如跳转到登录页面或提示用户重新认证。 引用[3]则提到两种用户场景:活跃用户应无感知刷新Token,而不活跃用户需要重新登录。这要求系统能够区分用户活跃状态,并设计相应的刷新策略,例如在每次请求时检查Token的有效期,快过期时自动刷新,或者在检测到Token过期后尝试用Refresh Token获取新Token。 接下来,我需要整合这些信息,形成一个分步骤的解决方案。首先,用户发起请求时,如果Token过期,应拦截请求,尝试用Refresh Token获取新Token。如果成功,则更新Token并重发原请求;如果失败,则跳转登录。同时,需要处理多个并发请求的情况,避免重复刷新Token。此外,还要考虑用户活跃状态下的自动刷新机制,例如定时刷新或根据Token有效期提前刷新。 可能需要注意的细节包括:如何存储和管理Token和Refresh Token,如何安全地发送刷新请求,如何处理异步操作和队列中的请求,以及错误处理逻辑,比如网络问题或服务器返回异常时的应对措施。此外,前端可能需要配合后端实现特定的刷新接口,并确保Refresh Token的安全存储,比如使用HttpOnly的Cookie来减少XSS攻击的风险。 然后,根据这些思考,我需要组织回答的结构,分步骤说明处理流程,并引用提供的参考资料来支持每个步骤。同时,生成相关问题需要考虑用户可能进一步想知道的内容,比如如何具体实现拦截请求、如何处理并发、如何设置Token有效期等。 最后,确保回答符合系统指令:使用正确的中文,数学公式用$...$,独立公式用$$...$$,虽然这个问题可能不涉及数学公式,但需要检查是否有其他格式要求。同时,生成的相关问题要自然且有针对性,帮助用户深入理解Token刷新的机制。</think>### 处理或刷新过期Token的认证机制步骤 #### 1. **Token过期检测与拦截请求** - 在每次发起API请求前,前端通过拦截器检查当前Token是否过期(例如通过解析JWT中的`exp`字段或后端返回的`401`状态码)[^3]。 - **实现示例**: ```javascript // 在axios拦截器中检查Token过期 axios.interceptors.request.use(config => { const token = localStorage.getItem('access_token'); if (token && isTokenExpired(token)) { return refreshTokenAndRetry(config); // 触发刷新Token逻辑 } return config; }); ``` #### 2. **使用Refresh Token获取新Token** - 若检测到Token过期,前端向认证服务器发送包含`Refresh Token`的请求(例如`/auth/refresh`接口),获取新的`Access Token`和`Refresh Token`[^1]。 - **安全要求**: - Refresh Token应通过安全通道(如HTTPS)传输。 - Refresh Token建议存储在`HttpOnly Cookie`中,避免XSS攻击[^2]。 #### 3. **处理并发请求与订阅队列** - 为避免多个请求同时触发刷新Token,需将后续请求加入**订阅队列**,待新Token获取后重新发起。 - **实现示例**: ```javascript let isRefreshing = false; let refreshSubscribers = []; function refreshTokenAndRetry(config) { if (!isRefreshing) { isRefreshing = true; return axios.post('/auth/refresh', { refresh_token }) .then(response => { localStorage.setItem('access_token', response.data.access_token); refreshSubscribers.forEach(cb => cb()); refreshSubscribers = []; return axios(config); // 重新发起原始请求 }) .finally(() => isRefreshing = false); } else { return new Promise(resolve => { refreshSubscribers.push(() => resolve(axios(config))); }); } } ``` #### 4. **自动刷新与用户状态管理** - **活跃用户**:通过定时器或每次请求时检查Token剩余有效期,若临近过期(如剩余5分钟),自动刷新Token[^3]。 - **不活跃用户**:若Token和Refresh Token过期,跳转至登录页面并要求重新认证。 #### 5. **异常处理与降级策略** - **Refresh Token失效**:清除本地Token并跳转登录页。 - **网络错误**:重试刷新机制(如最多重试3次),超出后提示用户检查网络。 --- ### 相关问题 1. **如何安全地存储Refresh Token?** (涉及Cookie的`HttpOnly`、`Secure`属性与前端存储风险) 2. **如何设计Token的有效期与刷新时间?** (平衡安全性与用户体验,如Access Token设为15分钟,Refresh Token设为7天) 3. **如何处理多个标签页同时触发Token刷新的问题?** (通过全局状态或BroadcastChannel同步刷新状态) --- ### 引用说明 [^1]: 通过订阅队列管理并发请求,确保Token刷新后重试所有挂起请求。 : Refresh Token失效时需强制用户重新登录,保障系统安全。 [^3]: 根据用户活跃状态动态调整刷新策略,平衡无感体验与安全性。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值