首页 > 其他分享 >实现无感刷新token

实现无感刷新token

时间:2023-11-17 16:23:28浏览次数:34  
标签:return 刷新 无感 token error data response

目录

需求

当 token 过期的时候,刷新 token,前端需要做到无感刷新 token,即刷 token 时要做到用户无感知,避免频繁登录。实现思路

方法一
后端返回过期时间,前端判断 token 过期时间,去调用刷新 token 接口
缺点:需要后端额外提供一个 token 过期时间的字段;使用了本地时间判断,若本地时间被篡改,特别是本地时间比服务器时间慢时,拦截会失败。

方法二
写个定时器,定时刷新 token 接口
缺点:浪费资源,消耗性能,不建议采用。

方法三
在响应拦截器中拦截,判断 token 返回过期后,调用刷新 token 接口

实现

axios 的基本骨架,利用 service.interceptors.response 进行拦截

import axios from "axios";

service.interceptors.response.use(
  (response) => {
    if (response.data.code === 409) {
      return refreshToken({
        refreshToken: localStorage.getItem("refreshToken"),
        token: getToken(),
      })
        .then((res) => {
          const { token } = res.data;
          setToken(token);
          response.headers.Authorization = `${token}`;
        })
        .catch((err) => {
          removeToken();
          router.push("/login");
          return Promise.reject(err);
        });
    }
    return response && response.data;
  },
  (error) => {
    Message.error(error.response.data.msg);
    return Promise.reject(error);
  }
);

问题解决

问题一:如何防止多次刷新 token

import axios from "axios";
// 是否正在刷新的标记
let isRefreshing = false;
service.interceptors.response.use(
  (response) => {
    if (response.data.code === 409) {
      if (!isRefreshing) {
        isRefreshing = true;
        return refreshToken({
          refreshToken: localStorage.getItem("refreshToken"),
          token: getToken(),
        })
          .then((res) => {
            const { token } = res.data;
            setToken(token);
            response.headers.Authorization = `${token}`;
          })
          .catch((err) => {
            removeToken();
            router.push("/login");
            return Promise.reject(err);
          })
          .finally(() => {
            isRefreshing = false;
          });
      }
    }
    return response && response.data;
  },
  (error) => {
    Message.error(error.response.data.msg);
    return Promise.reject(error);
  }
);

问题二:同时发起两个或者两个以上的请求时,其他接口怎么解决

当第二个过期的请求进来,token 正在刷新,先将这个请求存到一个数组队列中,想办法让这个请求处于等待中,一直等到刷新 token 后再逐个重试清空请求队列。那么如何做到让这个请求处于等待中呢?为了解决这个问题,得借助 Promise。将请求存进队列中后,同时返回一个 Promise,让这个 Promise 一直处于 Pending 状态(即不调用 resolve),此时这个请求就会一直等啊等,只要不执行 resolve,这个请求就会一直在等待。当刷新请求的接口返回来后,再调用 resolve,逐个重试。最终代码:

import axios from "axios";

// 是否正在刷新的标记
let isRefreshing = false;
//重试队列
let requests = [];
service.interceptors.response.use(
  (response) => {
    //约定code 409 token 过期
    if (response.data.code === 409) {
      if (!isRefreshing) {
        isRefreshing = true;
        //调用刷新token的接口
        return refreshToken({
          refreshToken: localStorage.getItem("refreshToken"),
          token: getToken(),
        })
          .then((res) => {
            const { token } = res.data;
            // 替换token
            setToken(token);
            response.headers.Authorization = `${token}`;
            // token 刷新后将数组的方法重新执行
            requests.forEach((cb) => cb(token));
            requests = []; // 重新请求完清空
            return service(response.config);
          })
          .catch((err) => {
            //跳到登录页
            removeToken();
            router.push("/login");
            return Promise.reject(err);
          })
          .finally(() => {
            isRefreshing = false;
          });
      } else {
        // 返回未执行 resolve 的 Promise
        return new Promise((resolve) => {
          // 用函数形式将 resolve 存入,等待刷新后再执行
          requests.push((token) => {
            response.headers.Authorization = `${token}`;
            resolve(service(response.config));
          });
        });
      }
    }
    return response && response.data;
  },
  (error) => {
    Message.error(error.response.data.msg);
    return Promise.reject(error);
  }
);

注意事项:

要注意的一点是,实际应用时,要注意:

  1. 刷新 token 时如果调接口,所使用的网络请求工具不能也使用这个封装的工具,否则就会陷入无限循环,可以使用简单未封装的方式请求。
  2. 本例使用的方法,是进行请求前刷新 token。也可以使用先调网络请求,如果接口返回错误码表示 token 过期,则刷新 token,再重新请求的方式。

标签:return,刷新,无感,token,error,data,response
From: https://www.cnblogs.com/wp-leonard/p/17839028.html

相关文章

  • 前端大文件上传如何做到刷新续传?
    前言这两天在学习阿里云oss上传。踩了不少坑,终于实现了大文件分片、断点续传的功能。这篇文章主要分享学习笔记,希望能给大家一些帮助。先看效果 技术栈1.前端:react+Ts+axios上传文件2.Node部分:定义接口、阿里云oss3.socket.io:实时同步上传进度特别说明axios中onUploadPr......
  • k8s中,如何通过token的方式,访问认证的kubelet的metrics指标?
     1、背景说明kubelet本身的10250端口,就提供了节点上的监控数据。 metricsserver可以进行访问。 但是,如果想要通过浏览器,或者curl命令进行访问,发现,是需要进行认证  [root@nccztsjb-node-02~]#curl-khttps://172.20.59.238:10250/metricsUnauthorized[root@n......
  • Angular 依赖注入系统里 Injection token APP_BASE_HREF 的使用场景
    Angular的依赖注入系统是其核心功能之一,它提供了一种优雅的方式来管理应用中的服务和组件之间的依赖关系。在Angular中,我们可以使用各种方式来提供依赖项,而APP_BASE_HREF是其中的一个依赖注入标记。APP_BASE_HREF是一个Injectiontoken,它在Angular的路由系统中扮演了重......
  • 数据量很大,字段频繁变化,数据频繁刷新,架构如何设计?
    对于大数据量、字段频繁变化、数据频繁刷新的情况,需要设计一个灵活而稳定的架构来有效地管理和处理数据。以下是关于这种情况下架构设计的详细描述:大数据量、字段频繁变化、数据频繁刷新的架构设计1.数据存储层面对于大数据量的情况,传统的关系型数据库可能无法很好地应对,因此......
  • token以及axios响应拦截器请求拦截器
    一、token的介绍1.概念访问权限的令牌,本质上是一串字符串2.创建正确登录后,由后端签发并返回3.作用判断是否有登录状态等,控制访问权限注意:前端只能判断有无token,而后端才能判断token的有效性4.使用目标:只有登录状态,才能访问内容页面1.在utils/auth.js中判断有无token令牌字符串,则强......
  • jmeter-set up先登录获取token,再测试
    1.顶部加通用的信息头管理,cookie管理器 2.添加setup线程组,用户数为13.添加登录请求4.添加断言,添加debug调试 5.提取json,  6.添加debug,运行后查看是否获取到token 7.设置token为全局变量 8.再添加线程组,线程组可正常设置并发数需要用到token的地方再添加......
  • 使用JWT、拦截器与ThreadLocal实现在任意位置获取Token中的信息,并结合自定义注解实现
    1.简介1.1JWTJWT,即JSONWebToken,是一种用于在网络上传递声明的开放标准(RFC7519)。JWT可以在用户和服务器之间传递安全可靠的信息,通常用于身份验证和信息交换。声明(Claims):JWT包含一组称为声明的信息,声明描述了一些数据。有三种类型的声明:注册声明(RegisteredClaims):这是......
  • :key可以不加,如果不加:key属性,刷新页面时,可能由于key相同,图片不刷新。
    件中使用vue-lazyload时,v-lazy代替v-bind:src实现图片懒加载可以使用key属性不是必须的,但是不写key可能报警告,有些内容可能显示异常。key的作用:提高性能,不影响显示效果(如果没有id,可以使用index代替)......
  • 关于Sa-token过程的一些解析
    1.Sa-Token是什么?Sa-Token是一个基于Java的轻量级身份认证和授权框架,用于简化和加强应用程序的用户认证和权限管理功能。它提供了一套简洁的API,使开发者可以轻松地实现用户登录、权限验证、会话管理等功能。2.如何生成token?根据用户id生成一串token字符串(一般来讲),......
  • Nacos-配置热更新【自动刷新】
    Nacos中的配置文件变更后,微服务无需重启就可以感知,通过下面两种配置实现:方式一:在@Value注入的变量所在类上添加注解@RefreshScope 方式二:使用@ConfigurationProperties注解读取配置文件内容packagecn.itcast.user.config;importlombok.Data;importorg.springframewo......