问题引入

在开发中为了安全或满足分布式场景,通常会舍弃原有的session认证手段,而采用jwt(json web token);但是使用token难免遇到token有效期的问题,如果token长期有效,服务端不断发布新的token,导致有效的token越来越多,这必然是存在安全问题的。而token不想session一样,在用户操作时会进行刷新,为了用户体验,这个刷新就需要自己实现。

方案

一、使用旧token获取新token

如果采取单个token的方式要实现token的自动刷新,就必须使用定时器,每隔一段时间自动刷新token,并且这个时候token一定要是没有过期的,因为如果已经过期的token也可以用来刷新,这和长期有效的token也没什么不同。但这种方式存在一定的问题:

  1. 为了保证同一时间,账户只被单个用户登录,后端必然要保证一个账户的多个token只有一个生效,最简单的方式就是使用分布式缓存中间件如redis,而存在并发请求时,可能前一个请求带着的是旧token,此时又到了刷新token的时间,就会产生请求的token与服务端存储的token不一致的问题
  2. 使用定时器是增加了性能的损耗,不是最佳的手段

二、使用双token的方式进行无感刷新

这里重点介绍这种方式,此方案的大致流程为:登录后客户端收到两个token(access_token,refresh_token),其中access_token用来鉴定身份,而refresh_token用来刷新access_token;这就要求了refresh_token要比access_token有效时间要长,并且refresh_token不能用来鉴定身份
使用这种方案又有一下解决方式:

  1. 后端每次响应都响应一个token过期时间,前端进行判断,在token过期前进行刷新,这种方式存在太多不可控因素,如客户端系统时间被修改、长时间没请求导致token过期却未刷新,无法做到无感刷新,并且也存在并发问题
  2. 使用定时器,使用定时器增加的资源的损耗,亏损了性能,不推荐
  3. 在得到token过期的请求时,再发送refresh_token;有点懒加载的意思,这种方案性能最优

具体实现(方案二的第三种方式)

流程

再理一理程序运行的流程:

  1. 首先,登录得到了两个token,并将其存起来
  2. 当access_token过期时,自动发送refresh_token到刷新token的请求路径请求token刷新
  3. 得到新的token之后,将请求重新发送,实现用户无感刷新token

代码

用到的工具函数

//判空
let isEmpty = function(obj) {return obj == null || obj == "undefined" || obj == "null" || new String(obj).trim() == '';
};
  1. 封装axios
const my_axios = axios.create({baseURL: '/app',timeout: 15000,withCredentials: true
});

这里对axios做一个简单的封装,不做过多赘述

  1. 定义请求拦截器,将请求带上token
my_axios.interceptors.request.use(req => {//判断当前是否存在tokenBo(tokenBo即两个token组成的对象),存在则带上token//isEmpty函数在上方的工具函数中if (!isEmpty(sessionStorage.getItem("tokenBo"))) {//通过请求路径中是否含有refershToken来判断当前是一般请求还是token刷新请求;从而在请求头中带上不同的tokenif (-req.url.indexOf("refreshToken") == 1) {req.headers['accessToken'] = JSON.parse(sessionStorage.getItem("tokenBo")).accessToken;} else {req.headers['refreshToken'] = JSON.parse(sessionStorage.getItem("tokenBo")).refreshToken;}}return req;},err => {return Promise.reject(err)})
  1. 响应拦截,进行token的无感刷新
    这是最难的一步,我们需要考虑到几个问题,第一:要实现无感,那么用户的请求就不能被舍弃,而是需要在得到新的token后帮他再执行一次;第二,当同时出现多个请求时,可能会导致多次刷新token的情况,所以需要用一个标志量来标志是否正在刷新token,并使用一个数据对请求进行存储
//标志当前是否正在刷洗token
let isNotRefreshing = true;
//请求队列
let requests = [];
my_axios.interceptors.response.use(async res => {//我们可以定义一个标准响应体,比如:{code=10415,msg='token已过期',data:null},当收到token过期的响应就要进行token刷新了if (res.data.code == 10415) {//首先拿到响应的配置参数,这和请求的配置参数是一样的,包括了url、data等信息,待会需要使用这个config来进行重发const config = res.config;//如果当前不处于刷新阶段就进行刷新操作if (isNotRefreshing) {isNotRefreshing = false;//返回刷新token的回调的返回值,本来考虑到由于请求是异步的,所以return会先执行,导致返回一个undefined,那么就需要使用async+await,但实际上没有加也成功了return my_axios.get("/admin/refreshToken").then(res => {//如果token无效或token仍然过期,就只能重新登录了if (res.code == 10422 || res.code == 10415) {sessionStorage.removeItem("tokenBo");sessionStorage.removeItem("currentAdmin");location.href = '/login';} else if (res.code == 10200) {//刷新成功之后,将新的token存起来sessionStorage.setItem("tokenBo", JSON.stringify(res.data))//执行requests队列中的请求,(requests中存的不是请求参数,而是请求的Promise函数,这里直接拿来执行就好)requests.forEach(run => run())//将请求队列置空requests = []//重新执行当前未执行成功的请求并返回return my_axios(config);}}).catch(() => {sessionStorage.removeItem("tokenBo");sessionStorage.removeItem("currentAdmin");location.href = '/';}).finally(() => {isNotRefreshing = true;})} else {//如果当前已经是处于刷新token的状态,就将请求置于请求队列中,这个队列会在刷新token的回调中执行,由于new关键子存在声明提升,所以不用顾虑会有请求没有处理完的情况,这段添加请求的程序一定会在刷新token的回调执行之前执行的return new Promise(resolve => {//这里加入的是一个promise的解析函数,将响应的config配置对应解析的请求函数存到requests中,等到刷新token回调后再执行requests.push(() => {resolve(my_axios(config));})})}} else {if (res.data.code == 10200) {return res.data;} else {if (res.data.code == 10409) {sessionStorage.removeItem("tokenBo");sessionStorage.removeItem("currentAdmin");location.href = "/#/login"}Message.error(res.data.message);return res.data;}}},err => {if (err && err.response && err.response.status) {switch (err.response.status) {case 404:Message.error("页面未找到");break;case 401:Message.error('没有权限访问')break;case 500:Message.error("系统维护中")break;case 505:Message.error("网络错误")}}}
)

关于实现token无感刷新的解决方案相关推荐

  1. 实现双token无感刷新

    流程 1.首先,登录得到了两个token,并将其存起来 2.当access_token过期时,自动发送refresh_token到刷新token的请求路径请求token刷新 3.得到新的token之后 ...

  2. token过期?页面如何实现无感刷新?

    我们为什么要无感刷新呢? 我们都知道,后台返回的token是有时效性的,时间到了,你在交互后台的时候,后台会判断你的token是否过期(安全需要),如果过期了就会通过邪恶的手段逼迫你重新登陆! 无感刷 ...

  3. Vue 无感刷新token

    关于无感刷新的理解:  实现token无感刷新对于前端来说是一项非常常用的技术,其本质是为了优化用户体验,当token过期时不需要用户跳回登录页重新登录,而是当token失效时,进行拦截,发送刷新to ...

  4. uniapp 实现无感刷新token, 适应大多数项目

    不管你是用taro uni 还是vue-cli 或者 react-cli 刷新token这块一通百通 本质上 都一样 我之前讲了一个是 在响应拦截哪里做token刷新 其实这样做还是不好的,因为这样我 ...

  5. 关于无感刷新Token,我是这样子做的

    本文正在参加「金石计划 . 瓜分6万现金大奖」 什么是JWT JWT是全称是JSON WEB TOKEN,是一个开放标准,用于将各方数据信息作为JSON格式进行对象传递,可以对数据进行可选的数字加密, ...

  6. 实现无感刷新token我是这样做的

    大家好,我是漫步,今天来分享一个登录常常遇到的难题,即登录超时时间与安全性的纠结问题. 原文: https://juejin.cn/post/6983582201690456071 前言 最近在做需求 ...

  7. 实现无感刷新 token 我是这样做的

    原文: https://juejin.cn/post/6983582201690456071 前言 最近在做需求的时候,涉及到登录token,产品提出一个问题:能不能让token过期时间长一点,我频繁 ...

  8. 使用Axios进行无感刷新Token

    前言 本人在开发项目时,在做登录模块时,参考了oauth2,在用户认证成功后会返回给前端一些令牌相关数据.接下来,再用进行接口请求时,前端根据令牌数据进行一系列的判断,然后做出最好的选择. 举个例子: ...

  9. Laravel6通过jwt(tymon/jwt-auth)实现API用户无感刷新TOKEN

    Laravel6通过jwt实现API用户无感刷新TOKEN 1.TOKEN是什么 2.jwt是什么 3.jwt安装&配置 3.1.通过composer安装 3.2.发布配置 3.3.生成加密密 ...

最新文章

  1. python和c学习-关于本站 - cPython - 给大学生的入门教程
  2. MATLAB应用实战系列NSGA-II多目标优化算法原理及应用实例(附MATLAB代码)
  3. 搭建第一个SpringBoot工程;SpringBoot整合mybatis;SpringBoot整合Redis-cluster集群;SpringBoot整合EhCache;
  4. Boost:bind绑定的回归测试
  5. QT的QScroller类的使用
  6. IOS-awakeFromNib和viewDidLoad
  7. asp.net Viewstate 优化
  8. [转载] python程序所需的图片通过base64编码成字符串放在代码中
  9. 如何运行导入的项目_从0到1学习Flink》—— Flink 项目如何运行?
  10. [设计模式] 15 解释器模式 Interpreter
  11. 什么是PXE及PXE作用
  12. 手游最佳搭档:高续航音质卓越,高颜值精品蓝牙耳机推荐
  13. Springboot+jwt+shiro实现用户权限控制
  14. 流媒体 直播细节优化
  15. USB-HID游戏手柄的数据通信
  16. poj3580:SuperMemo(块状链表/Splay)
  17. 【愚公系列】2022年10月 .Net Core使用cpolar内网穿透功能实现钉钉回调事件的监听
  18. ug编程内公差和外公差是什么_ug编程内外公差是什么
  19. Hive大总结!!!
  20. Nand Flash基础知识

热门文章

  1. Java Math.log10()方法
  2. java开发一天的工作量_java预估工作量
  3. 【C#】[网络]WebBrowser网页操作之提取获取元素和标签(完整篇)
  4. python学习 -- 菜鸟教程+B站视频
  5. 企业年会直播来个虚拟舞台场景如何?
  6. 知道这几个计算就掌握了SVPWM
  7. 亚马逊服务器 修改密钥对,更改ec2实例的密钥对
  8. Python网络编程之二:网络编程基础
  9. 重要通知 | 2022年互联网地图类软件必须“持证上岗”
  10. Chrome谷歌浏览器怎么添加鼠标手势