js无痛刷新Token的实现

这个需求场景很常见,几乎很多项目都会用上,之前项目也实现过,最近刚好有个项目要实现,重新梳理一番。

需求

对于需要前端实现无痛刷新Token,无非就两种:

  • 请求前判断Token是否过期,过期则刷新
  • 请求后根据返回状态判断是否过期,过期则刷新

处理逻辑

实现起来也没多大差别,只是判断的位置不一样,核心原理都一样:

  • 判断Token是否过期

    没过期则正常处理

    过期则发起刷新Token的请求

    拿到新的Token保存

    重新发送Token过期这段时间内发起的请求

重点:

  • 保持Token过期这段时间发起请求状态(不能进入失败回调)
  • 把刷新Token后重新发送请求的响应数据返回到对应的调用者

实现

  • 创建一个flag isRefreshing 来判断是否刷新中
  • 创建一个数组队列retryRequests来保存需要重新发起的请求
  • 判断到Token过期isRefreshing = false 的情况下 发起刷新Token的请求

    刷新Token后遍历执行队列retryRequests

    isRefreshing = true 表示正在刷新Token,返回一个Pending状态的Promise,并把请求信息保存到队列retryRequests

import axios from "axios";
import Store from "@/store";
import Router from "@/router";
import { Message } from "element-ui";
import UserUtil from "@/utils/user";

// 创建实例
const Instance = axios.create();
Instance.defaults.baseURL = "/api";
Instance.defaults.headers.post["Content-Type"] = "application/json";
Instance.defaults.headers.post["Accept"] = "application/json";

// 定义一个flag 判断是否刷新Token中
let isRefreshing = false;
// 保存需要重新发起请求的队列
let retryRequests = [];

// 请求拦截
Instance.interceptors.request.use(async function(config) {
  Store.commit("startLoading");
  const userInfo = UserUtil.getLocalInfo();
  if (userInfo) {
    //业务需要把Token信息放在 params 里面,一般来说都是放在 headers里面
    config.params = Object.assign(config.params ? config.params : {}, {
      appkey: userInfo.AppKey,
      token: userInfo.Token
    });
  }
  return config;
});

// 响应拦截
Instance.interceptors.response.use(
  async function(response) {
    Store.commit("finishLoading");
    const res = response.data;
    if (res.errcode == 0) {
      return Promise.resolve(res);
    } else if (
      res.errcode == 30001 ||
      res.errcode == 40001 ||
      res.errcode == 42001 ||
      res.errcode == 40014
    ) {
    // 需要刷新Token 的状态 30001 40001 42001 40014
    // 拿到本次请求的配置
      let config = response.config;
    //   进入登录页面的不做刷新Token 处理
      if (Router.currentRoute.path !== "/login") {
        if (!isRefreshing) {
            // 改变flag状态,表示正在刷新Token中
          isRefreshing = true;
        //   刷新Token
          return Store.dispatch("user/relogin")
            .then(res => {
            // 设置刷新后的Token
              config.params.token = res.Token;
              config.params.appkey = res.AppKey;
            //   遍历执行需要重新发起请求的队列
              retryRequests.forEach(cb => cb(res));
            //   清空队列
              retryRequests = [];
              return Instance.request(config);
            })
            .catch(() => {
              retryRequests = [];
              Message.error("自动登录失败,请重新登录");
                const code = Store.state.user.info.CustomerCode || "";
                // 刷新Token 失败 清空缓存的用户信息 并调整到登录页面
                Store.dispatch("user/logout");
                Router.replace({
                  path: "/login",
                  query: { redirect: Router.currentRoute.fullPath, code: code }
                });
            })
            .finally(() => {
                // 请求完成后重置flag
              isRefreshing = false;
            });
        } else {
          // 正在刷新token,返回一个未执行resolve的promise
          // 把promise 的resolve 保存到队列的回调里面,等待刷新Token后调用
          // 原调用者会处于等待状态直到 队列重新发起请求,再把响应返回,以达到用户无感知的目的(无痛刷新)
          return new Promise(resolve => {
            // 将resolve放进队列,用一个函数形式来保存,等token刷新后直接执行
            retryRequests.push(info => {
                // 将新的Token重新赋值
              config.params.token = info.Token;
              config.params.appkey = info.AppKey;
              resolve(Instance.request(config));
            });
          });
        }
      }
      return new Promise(() => {});
    } else {
      return Promise.reject(res);
    }
  },
  function(error) {
    let err = {};
    if (error.response) {
      err.errcode = error.response.status;
      err.errmsg = error.response.statusText;
    } else {
      err.errcode = -1;
      err.errmsg = error.message;
    }
    Store.commit("finishLoading");
    return Promise.reject(err);
  }
);

export default Instance;

到此这篇关于js无痛刷新Token的实现的文章就介绍到这了,更多相关js无痛刷新Token内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解uniapp无痛刷新token方法

    前端在请求接口时,和后端定义好了,如果状态码为 401 ,则表明 token 过期,需要前端请求新的 token 大概流程如下: 1.用户登录之后,后端会返回两个 token ,分别为accessToken 和refreshToken 存储到Storage 平时请求数据时,请求头使用accessToken 来发送接口 2.当返回401 token 过期后, 我们通过接口向后端获取新的 token ,请求参数为refreshToken 3.我们拿到新的accessToken 和refreshTok

  • axios如何利用promise无痛刷新token的实现方法

    需求 最近遇到个需求:前端登录后,后端返回token和token有效时间,当token过期时要求用旧token去获取新的token,前端需要做到无痛刷新token,即请求刷新token时要做到用户无感知. 需求解析 当用户发起一个请求时,判断token是否已过期,若已过期则先调refreshToken接口,拿到新的token后再继续执行之前的请求. 这个问题的难点在于:当同时发起多个请求,而刷新token的接口还没返回,此时其他请求该如何处理?接下来会循序渐进地分享一下整个过程. 实现思路 由于

  • js无痛刷新Token的实现

    这个需求场景很常见,几乎很多项目都会用上,之前项目也实现过,最近刚好有个项目要实现,重新梳理一番. 需求 对于需要前端实现无痛刷新Token,无非就两种: 请求前判断Token是否过期,过期则刷新 请求后根据返回状态判断是否过期,过期则刷新 处理逻辑 实现起来也没多大差别,只是判断的位置不一样,核心原理都一样: 判断Token是否过期 没过期则正常处理 过期则发起刷新Token的请求 拿到新的Token保存 重新发送Token过期这段时间内发起的请求 重点: 保持Token过期这段时间发起请求状

  • JS实现刷新父页面不弹出提示框的方法

    本文实例讲述了JS实现刷新父页面不弹出提示框的方法.分享给大家供大家参考,具体如下: A页面 open方式出 B页面 ,当B页面做了类如保存动作后,需要关闭B页面,刷新A页面的情况下,会弹出一个提示框,要求点重试,这个就是发生预料之外的情况,用户体验很差. 解决方案分两种情况: 1.A页面很简单的情况(没有frame/iframe) 在B页面中的function中: function close(){ window.opener.location.reload(); window.opener

  • js实现刷新iframe的方法汇总

    javascript实现刷新iframe的方法的总结,现在假设存在下面这样一个iframe,则刷新该iframe的N种方法有: 复制代码 代码如下: <iframe src="1.htm" name="ifrmname" id="ifrmid"></iframe> 第一种方法:用iframe的name属性定位 复制代码 代码如下: <input type="button" name="B

  • 利用php-cli和任务计划实现刷新token功能的方法

    1.业务需求 需要实现这样一个功能:在第三方授权的认证当中,在用户首次登录授权我们会得到一个access_token,有效期为25小时,还会得到一个refresh_token,有效期为30天. 我们只要保存好这个refresh_token,在30天内我们都可以用这个refresh_token去请求一个api,他会返回一个新的access_token.这样我们只需要让用户授权一次,我们就可以获得长达30天的一个授权期限. 这里可以分为几个点: <1>这个应该是要定期执行的一个任务. 25小时才会

  • js实现刷新页面后回到记录时滚动条的位置【两种方案可选】

    当div中绑定数据,给它一个属性overflow-y: scroll,添加长度大小,使其能够出现滚动条:每次刷新的时候滚动条总是会出现在最上方,这使我很头疼,经过查阅网上资料,返现两种方法可行.如下: 第一种方案 将上一个页面的div的scrolltop距离长度记录在cookie中,然后通过js调整刷新页面时的长度记录,代码如下: js代码: <script> var _h = 0; function SetH(o) { _h = o.scrollTop SetCookie("a&q

  • 用js来刷新当前页面保留参数的具体实现

    网站有了一定流量之后,就需要想办法提高网站的粘性,世界买家网最近打算弄一组外贸工具,就是一些常用的工具,比如世界时间查询http://www.buyerinfo.biz/tools/global-time/,hscode查询,汇率查询和换算等等,有几个已经完工,大家看下咯,多提提意见.其中还有js的刷新,不是很会弄,于是记录下, reload 方法,该方法强迫浏览器刷新当前页面. 语法: location.reload([bForceGet]) 参数: bForceGet, 可选参数, 默认为

  • JS关于刷新页面的相关总结

    很多程序员无论是新手还是老的程序员都避免不了关于JS刷新页面的相关内容,在本文中我们整理了我们总结的关于JS页面刷新的相关重要知识点文章,一起来跟着学习下. JS刷新当前页面的几种方法总结 reload 方法,该方法强迫浏览器刷新当前页面 replace 方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,你不能通过"前进"和"后退"来访问已经被替换的URL 返回并刷新页面 自动刷新页面的方法 js刷新页面方法大全 页面

  • 解决laravel-admin 自己新建页面里 js 需要刷新一次的问题

    主要是参考laravel-admin 自定义图表 的方法,下面简要介绍一下 1.将需要用到的需要引用的插件采用下面的方法引入: 用echart.js举例,首先要下载echart.js,放到public目录下面,比如放在public/vendor/echart.js目录,然后在app/Admin/bootstrap.php引入组件: use Encore\Admin\Facades\Admin; Admin::js('/vendor/chartjs/dist/echart.js'); 注意:css

随机推荐