0


前端双token无感刷新详解

前端双token无感刷新详解

image-20240804125950784

前端双token无感刷新详解

1.双token的使用场景

众所周知,

  1. Token

作为用户获取受保护资源的凭证,必须设置一个过期时间,否则一次登录便可永久使用,认证功能就失去了意义。但是矛盾在于:过期时间设置得太长,用户数据的

  1. 安全性

将大打折扣;过期时间设置得太短,用户就必须每隔一段时间

  1. 重新登录

,以获取新的凭证,这会极大挫伤用户的积极性。

1.假如你正在访问某个平台,沉浸式的使用了一小时后却突然弹出一个token过期,重定向到了登录页…

2.假如你正在某网站里填写你的个人信息或者内容繁多的表单选项,填到一半的时候token过期了…提示需要重新登录,结果登录回来之后又要重新填写表单信息…T~T

3.假如你正在激情的抢票、蹲卡点秒杀活动!结果刚要抢购却因token过期被重定向到登录页…

是不是代入起来就已经很头疼了?那如何避免这种情况发生呢,接下来我们就引入今天的主角——

  1. 无感刷新token

2.什么是token

在介绍

  1. token无感刷新

之前,我们先简单介绍一下什么是

  1. token

吧!

token是一种用户标识,表示用户身份,类似于我们的身份证件。

  1. Token

是在服务端产生的。如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回

  1. Token

给前端。前端可以在每次请求的时候带上

  1. Token

证明自己的合法地位。如果这个

  1. Token

在服务端持久化(比如存入数据库),那它就是一个永久的身份令牌。

3.如何无感刷新token(图文+代码)

  1. token

过期了之后,我们应该如何做到让用户无感知的自动刷新

  1. token

呢?

目前比较常见的有三种方法:

  • 写个定时器,时间一到就刷新getToken接口来获取新token(极其不推荐):消耗性能,不断的去发送网络请求,不建议这种做法
  • 后端返回一个过期时间,前端每次发送请求都去判断token是否过期,如果过期就调用getToken接口来获取新token(不推荐):需要后端额外提供一个过期时间的字段;使用了本地时间判断,若本地时间被篡改,特别是本地时间比服务器时间慢时,拦截会失败。
  • 在请求响应拦截器中拦截,判断token 返回过期后,调用刷新token接口。(推荐⭐⭐⭐):无性能损耗

在登录成功后,后端会返回两个

  1. Token

,一个是

  1. Access Token

(即短token),一个是

  1. Refresh Token

(即长token)。前端需要将这两个

  1. Token

保存到本地存储中,例如

  1. localStorage

  1. sessionStorage

中,以便在需要时使用。

当需要访问API时,前端将从本地存储中获取

  1. Access Token

,并将其放入请求头中发送到后端。如果

  1. Access Token

过期了,后端会返回一个错误响应,并提示前端进行刷新

  1. Token

的操作。

前端可以使用下面的代码实现刷新Token的操作:

  1. import axios from"axios";import{ getToken, setToken, setRefreshToken, getRefreshToken }from"./token";import{ ElMessage }from"element-plus";import router from"@/router";import{ refreshToken }from"./refreshtoken";const service = axios.create({baseURL:"http://localhost:8087",headers:{Authorization:`Bearer ${getToken()}`,},});// 添加请求拦截器
  2. service.interceptors.request.use(function(config){if(config.url ==="/refresh_token"){
  3. config.headers["Authorization"]=`Bearer ${getRefreshToken()}`;}// 在发送请求之前做些什么return config;},function(error){// 对请求错误做些什么return Promise.reject(error);});// 添加响应拦截器
  4. service.interceptors.response.use(async(res)=>{// 1.第一次登录了以后 后台在header里面返回了短token 那么要先接收token存储到localstorage里面if(res.headers.authorization){const token = res.headers.authorization.replace("Bearer ","");// 设置短tokensetToken(token);
  5. service.defaults.headers.Authorization =`Bearer ${token}`;}// 2. 第一次登录了以后 后台在header里面返回了长token 那么要先接受长token 存储到localstorage里面if(res.headers.refreshtoken){const refreshtoken = res.headers.refreshtoken.replace("Bearer ","");// 设置长tokensetRefreshToken(refreshtoken);}// 3 假设当后端返回401的时候 代表token失效 时间到了 这个时候正常处理就是跳到登录页面// 但是在实际的业务场景的时候 用户体验非常不好if(res.data.code ===401){// ElMessage({// message: "token失效",// type: "warning",// });// router.push("/login");// 这个地方就是短token失效了 提交表单 不跳到登录页面 因为这样用户体验很不好// 请求刚刚定义的一个接口// 这个时候 提交表单的接口 还没提交 停在这里了 现在要干啥?请求// 干啥了? 请求了changtoken携带过去 刷新token的接口const success =awaitrefreshToken();if(success){
  6. res.config.headers.Authorization =`Bearer ${getToken()}`;const result =await service.request(res.config);return result;}}return res.data;});functionrequest(options){
  7. options.method = options.method ||"get";// 关于get请求参数的调整if(options.method.toLowerCase()==="get"){
  8. options.params = options.data;}returnservice(options);}exportdefault request;

对应的

  1. token.js

  1. refreshtoken.js

代码:

模拟网卡的时候,响应数据还没返回就重复刷新的情况:

  1. (本质就是借助Promise。将请求存进队列中后,同时返回一个Promise,让这个Promise一直处于Pending状态(即不调用resolve),此时这个请求就会一直等啊等,只要我们不执行resolve,这个请求就会一直在等待。当刷新请求的接口返回来后,我们再调用resolve,逐个重试。)

image-20240804174825753

image-20240804144509819

  1. token

存进

  1. localstorage

里,同时为了防止多次刷新

使用

  1. Token

  1. Refresh Token

的时序图如下:

1)登录

图片描述

2)业务请求
图片描述

3)Token 过期,刷新 Token
图片描述

现在,我们就已经成功实现了长短token的无感刷新啦!

一定要理解文章开头这张图的流程哦


image-20240804125950784


本文转载自: https://blog.csdn.net/weixin_46714216/article/details/140909244
版权归原作者 真的很上进 所有, 如有侵权,请联系我们删除。

“前端双token无感刷新详解”的评论:

还没有评论