赞
踩
这个需求场景很常见,几乎很多项目都会用上,之前项目也实现过,最近刚好有个项目要实现,重新梳理一番。
对于需要前端实现无痛刷新Token,无非就两种:
实现起来也没多大差别,只是判断的位置不一样,核心原理都一样:
Token
是否过期
Token
的请求
Token
保存Token
过期这段时间内发起的请求重点:
Token
过期这段时间发起请求状态(不能进入失败回调)Token
后重新发送请求的响应数据返回到对应的调用者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/refreshToken")
- .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;
-

文笔不怎么样,谅
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。