前端实现token的无感刷新#记录

因为服务器的token一版不会设置太长,token过期后就需要重新登录,频繁的登录会造成体验不好的问题,因此,需要体验好的话,就需要定时去刷新token,并替换之前的token。以下是token失效的效果:

那么做到token的无感刷新,主要有3种方案:

方案一:

后端返回过期时间,前端每次请求就判断token的过期时间,快到过期时间,就去调用刷新token接口。

缺点:若本地时间被篡改,特别是本地时间比服务器时间慢时,拦截会失败,不建议采用。

方案二:

后端返回过期时间,前端写个定时器,然后定时刷新token接口。

缺点:浪费资源,消耗性能,不建议采用。

方案三: 

在请求响应拦截器中拦截,判断token 返回过期后,调用刷新token接口,推荐使用。

那么以下是方案三的实现步骤:

1、首次登录的时候会获取到两个token,把获取到的两个token存到前端缓存中。

localStorage.setItem('refreshToken',xxx) 
localStorage.setItem('token', xxx)

2、token刷新基础写法

import axios from "axios"

let flag = false // 设置开关,防止多次请求
let expiredList = [] //存储过期的请求

// 把过期请求添加在数组中
function addExpiredRequest(request) {
	expiredList.push(request)
}


// 创建axios实例
const service = axios.create({
    baseURL: 'http://xxxxxxxxx', //后台请求接口路径
    timeout: 10000  // 请求超时时间
})

service.interceptors.request.use(
  config => {
    localStorage.getItem('token') && (config.headers.Authorization = localStorage.getItem('token'))
    config.headers['Content-Type'] = config.headers['Content-Type'] || 'application/json;charset=utf-8'
    return config
  },
  error => {
    Promise.reject(error)
  }
)

service.interceptors.response.use(res => {
  const code = res.data.code
  if (code === 70001 || code === 401) { //与后端约定相应的code以此判断是否过期
    // 把过期请求存储起来,用于请求到新的刷新token
		addRequest(() => resolve(http(config)))
      // 用刷新token去请求新的主token
      if (!flag) {
		    flag = true;
		    // 获取刷新token
		    let refreshToken = localStorage.getItem('refreshToken');
		    if (refreshToken) {
			    // 判断刷新token是否过期,  getRefreshToken这个是你封装的请求刷新token的接口
			    getRefreshToken(refreshToken).then(res => {
				    if (判断刷新token失效,退出登录) {
					    flag = false
					    // 移除刷新token
                        localStorage.removeItem('refreshToken')
                        localStorage.removeItem('token')
                        location.href = '/' //跳转到你指定的页面
				    } else if (res.code === 1000) {
					    flag = false
					    // 重新发送请求
					    expiredList.forEach((request) => request())
                        expiredList = [] 
				    }
			    })
		    }
	    }
  }else if(code === 200){
    return res.data
  }
},
  error => {
    return Promise.reject(error)
  }
)

export default service

3、token刷新进阶写法(包含防止多次刷新token、同时发起两个或者两个以上的请求时刷新token)

import axios from 'axios'

// 是否正在刷新的标记
let isRefreshing = false

//存储过期的请求
let expiredList = [] 

// 创建axios实例
const service = axios.create({
    baseURL: 'http://xxxxxxxxx', //后台请求接口路径
    timeout: 10000  // 请求超时时间
})

service.interceptors.request.use(
  config => {
    localStorage.getItem('token') && (config.headers.Authorization = localStorage.getItem('token'))
    config.headers['Content-Type'] = config.headers['Content-Type'] || 'application/json;charset=utf-8'
    return config
  },
  error => {
    Promise.reject(error)
  }
)

service.interceptors.response.use(
  res => {
  //约定code 401 token 过期
    if (res.data.code === 401) {
      if (!isRefreshing) {
        isRefreshing = true
        //调用刷新token的接口
        return refreshToken({ refreshToken: localStorage.getItem('refreshToken'), token: localStorage.getItem('token') }).then(res => {
          const { token } = res.data
          // 替换token
          localStorage.setItem('token', token)
          res.headers.Authorization = `${token}`
          // token 刷新后将数组的方法重新执行
          expiredList.forEach((cb) => cb(token))
          expiredList = [] // 重新请求完清空
          return service(res.config)
        }).catch(err => {
          localStorage.removeItem('refreshToken')
          localStorage.removeItem('token')
          location.href = '/' //跳转到你指定的页面
          return Promise.reject(err)
        }).finally(() => {
          isRefreshing = false
        })
      } else {
        // 返回未执行 resolve 的 Promise
        return new Promise(resolve => {
          // 用函数形式将 resolve 存入,等待刷新后再执行
          expiredList.push(token => {
            res.headers.Authorization = `${token}`
            resolve(service(res.config))
          })
        })
      }
    }
    return res && res.data
  },
  (error) => {
    return Promise.reject(error)
  }
)

4、上述写法是方便在此文章中理解无感刷新token的代码,其中储存token到本地缓存中的代码可以提取出来,然后在使用到的地方导入使用即可。

例子:把需要的方法提取出来

export function getRefreshTokenn() {
  return localStorage.getItem(refreshToken)
}

export function setRefreshToken(token) {
  return localStorage.setItem('refreshToken',token)
}

export function removeRefreshToken() {
  return localStorage.removeItem('refreshToken')
}

使用方法:
import { getRefreshTokenn, setRefreshToken, removeRefreshToken } from '@/utils/auth'

把 localStorage.removeItem('refreshToken') 这段代码替换成 removeRefreshToken()

相关推荐

  1. 前端实现token刷新原因和步骤

    2024-04-08 12:58:02       54 阅读
  2. 前端实现token刷新技术方案

    2024-04-08 12:58:02       46 阅读
  3. 刷新token

    2024-04-08 12:58:02       46 阅读
  4. token刷新

    2024-04-08 12:58:02       32 阅读
  5. token刷新

    2024-04-08 12:58:02       26 阅读
  6. token刷新方法

    2024-04-08 12:58:02       32 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-08 12:58:02       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-08 12:58:02       106 阅读
  3. 在Django里面运行非项目文件

    2024-04-08 12:58:02       87 阅读
  4. Python语言-面向对象

    2024-04-08 12:58:02       96 阅读

热门阅读

  1. windows 环境下docker打包python项目

    2024-04-08 12:58:02       41 阅读
  2. 基于单片机的风向风速传感器防冻装置设计

    2024-04-08 12:58:02       35 阅读
  3. 网络入门基础

    2024-04-08 12:58:02       30 阅读
  4. ISBN信息查询api接口

    2024-04-08 12:58:02       40 阅读
  5. 【云开发笔记NO.25】缓存和技术中台

    2024-04-08 12:58:02       31 阅读
  6. SpringBoot 使用redis

    2024-04-08 12:58:02       32 阅读
  7. 这个开源项目,支持中文版啦~

    2024-04-08 12:58:02       33 阅读
  8. SSL根证书是什么

    2024-04-08 12:58:02       40 阅读
  9. Vue3_2024_10天【Vue2和Vue3父传子,使用props的区别】

    2024-04-08 12:58:02       39 阅读
  10. ThreadLocal该何时注入值?

    2024-04-08 12:58:02       34 阅读
  11. python项目练习——18.文件加密和解密工具

    2024-04-08 12:58:02       39 阅读