关于解决token过期失效问题

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

一、先认识下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
四、小结

在这里插入图片描述

### 解决APP后台运行导致的Token过期问题 在APP后台运行时,Token可能因超时而失效。为了解决这一问题,可以采用以下方法: #### 1. 使用刷新Token机制 通过引入刷新Token(Refresh Token)机制,可以在主Token(Access Token过期时,利用刷新Token向服务器请求新的主Token。这种方式能够避免用户频繁重新登录[^1]。 ```javascript // 示例:刷新Token逻辑 function refreshToken() { return fetch('/api/refresh-token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refreshToken: localStorage.getItem('refreshToken') }) }).then(response => response.json()) .then(data => { localStorage.setItem('accessToken', data.accessToken); return data.accessToken; }); } ``` #### 2. 利用Promise解决多请求冲突 当多个接口同时请求并遇到Token过期时,可以通过Promise机制确保所有请求等待新的Token刷新完成后继续执行。这种方法可以有效避免重复刷新Token问题[^1]。 ```javascript let isRefreshing = false; // 刷新Token开关 let refreshSubscribers = []; // 存储需要等待刷新完成的请求 function handleTokenExpired(error) { const retryOriginalRequest = new Promise((resolve) => { refreshSubscribers.push(() => { resolve(); }); }); if (!isRefreshing) { isRefreshing = true; refreshToken().then(() => { isRefreshing = false; refreshSubscribers.forEach(cb => cb()); refreshSubscribers = []; }); } return retryOriginalRequest; } ``` #### 3. Token组成与安全性 为了提高Token的安全性,可以将用户ID(uid)、时间戳(time)和签名(sign)作为Token的基本组成部分[^2]。此外,还可以在Token中加入不变参数以减少数据库查询次数。 ```javascript // 示例:生成Token function generateToken(uid, time, secretKey) { const tokenData = `${uid}:${time}`; const sign = CryptoJS.HmacSHA256(tokenData, secretKey).toString(CryptoJS.enc.Hex); return `${tokenData}:${sign}`; } ``` #### 4. 接口封装与自动刷新Token 在实际开发中,可以通过对接口进行封装,自动处理Token刷新逻辑。例如,在uni-app中可以创建一个`app.js`文件来管理API请求和Token刷新逻辑[^3]。 ```javascript // 示例:uni-app接口封装 function request(url, method, data) { return new Promise((resolve, reject) => { uni.request({ url, method, data, header: { 'Authorization': `Bearer ${localStorage.getItem('accessToken')}` }, success(res) { if (res.statusCode === 401) { // Token过期 handleTokenExpired().then(() => { // 重新发送请求 resolve(request(url, method, data)); }); } else { resolve(res.data); } }, fail(err) { reject(err); } }); }); } ``` --- ### 总结 通过引入刷新Token机制、利用Promise解决多请求冲突、优化Token组成以及对接口进行封装,可以有效解决APP后台运行时Token过期问题。这些方法不仅提高了用户体验,还增强了系统的安全性和稳定性。 ---
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值