React-umi-request動態刷新Token功能實現及node.js 代碼邏輯


在Antd-pro里面,使用的是umi-request,為了實現動態刷新token,我使用了攔截器。

攔截器更新token有兩種:

方法一:在請求發起前攔截每個請求,判斷token的有效時間是否已經過期,若已過期,則將請求掛起,先刷新token后再繼續請求。

  • 優點: 在請求前攔截,能節省請求,省流量。
  • 缺點: 需要后端額外提供一個token過期時間的字段 refreshTime ;使用了本地時間判斷,若本地時間被篡改,特別是本地時間比服務器時間慢時,攔截會失敗。

方法二:攔截返回后的數據。先發起請求,接口返回過期后,先刷新token,再進行一次重試。

  • 優點:不需額外的token過期字段,不需判斷時間。
  • 缺點: 會消耗多一次請求,耗流量。

綜上考慮,方法一和二優缺點是互補的,方法一有校驗失敗的風險(本地時間被篡改時,當然一般沒有用戶閑的蛋疼去改本地時間的啦),方法二更簡單粗暴,等知道服務器已經過期了再重試一次,只是會耗多一個請求。

我看有的博主用了方法二,我個人更喜歡方法一,(原因就是有的請求要求並發,for循環,一次出去10個請求,所以嘛。。。)

下面是實現過程

/**
 * request 網絡請求工具
 * 更詳細的 api 文檔: https://github.com/umijs/umi-request
 */
import { extend } from 'umi-request';
import { notification } from 'antd';
import { routerRedux } from 'dva/router';
import { getUserToken, saveUserToken, clearAuthority } from './authority';
import jwt_decode from 'jwt-decode'
const codeMessage = {
  200: '服務器成功返回請求的數據。',
  201: '新建或修改數據成功。',
  202: '一個請求已經進入后台排隊(異步任務)。',
  204: '刪除數據成功。',
  400: '發出的請求有錯誤,服務器沒有進行新建或修改數據的操作。',
  401: '用戶沒有權限(令牌、用戶名、密碼錯誤)。',
  403: '用戶得到授權,但是訪問是被禁止的。',
  404: '發出的請求針對的是不存在的記錄,服務器沒有進行操作。',
  406: '請求的格式不可得。',
  410: '請求的資源被永久刪除,且不會再得到的。',
  422: '當創建一個對象時,發生一個驗證錯誤。',
  500: '服務器發生錯誤,請檢查服務器。',
  502: '網關錯誤。',
  503: '服務不可用,服務器暫時過載或維護。',
  504: '網關超時。',
};
/**
 * 異常處理程序
 */

const errorHandler = error => {
  const { response } = error;

  if (response && response.status) {
    const errorText = codeMessage[response.status] || response.statusText;
    const { status, url } = response;
    notification.error({
      message: `請求錯誤 ${status}: ${url}`,
      description: errorText,
    });
  } else if (!response) {
    notification.error({
      description: '您的網絡發生異常,無法連接服務器',
      message: '網絡異常',
    });
  }

  return response;
};
/**
 * 配置request請求時的默認參數
 */

const request = extend({
  errorHandler,
  // 默認錯誤處理
  credentials: 'include', // 默認請求是否帶上cookie
});

let _cacheRequest = [];
let guoqi = true;
let ispending = false;
request.interceptors.request.use(async (url, options) => {

    const token = getUserToken();
    if( token ){
        //如果有token 就走token邏輯
        const headers = {
            Authorization: `Bearer ${token}`,
        };
        //如果是刷新token接口,就直接過,不要攔截它!!!
        if( url === '/weiqinketop/api/account/login/getnewtoken'){
            return ({
                url: url,
                options: { ...options, headers: headers },
            });
        }
        let decodeToken = jwt_decode(token);
        const { iat, exp, refreshTime } = decodeToken;
        const maxTime = exp*1000+refreshTime;
        const nowTime = new Date().getTime();
        console.log( parseInt((maxTime - nowTime)/1000) +'秒后重新登錄')
        if( nowTime >= maxTime){
            //token過期,而且延期token也過去了,那么,清空你的數據,直接返回登錄,不允許操作了
            console.log('超時了')
            clearAuthority()
            location.href = '/user/login'
            return;
        }
        console.log( parseInt((exp*1000 - nowTime)/1000) + '秒后第一個過期' )
        if( nowTime >= exp*1000 ){
            //只是過期了,那就去拿新的token
            if( ispending ){
                //如果正在發送中,此請求就等一會吧,生成一個Promise 等新token返回的時候,我再resolve
            }else{
                //如果沒發送,立刻改為發送狀態
                ispending = true;
                //如果過期了還沒請求新token,那就請求新token 記得帶上舊token
                request('/weiqinketop/api/account/login/getnewtoken',{
                    method: 'POST',
                    data:{}    
                }).then((res)=>{
                    if( res.status === 200 ){
                        ispending = false;
                        let token = res.token;
                        saveUserToken( token )
                        _cacheRequest.map(cb => cb())
                    }
                })

            }
            return new Promise((resolve, reject) => {
                _cacheRequest.push(() => {
                    resolve()
                })
            });
        }
        return ({
            url: url,
            options: { ...options, headers: headers },
        });
    }
    return ({
        url: url,
        options: options,
    });
})
//第二個攔截器,為什么會存在第二個攔截器呢?就是因為第一個攔截器有可能返回Promise,那么Promise由第二個攔截器處理吧。之前因為這個問題跟umi提了issues。原來是我沒搞明白。。。
request.interceptors.request.use(async (url, options) => {
    const token = getUserToken();
    if( token ){
        //如果有token 就走token邏輯
        const headers = {
            Authorization: `Bearer ${token}`,
        };
        return ({
            url: url,
            options: { ...options, headers: headers },
        });
    }
    return ({
        url: url,
        options: options,
    });



})
export default request;

說下中途遇到的問題吧,原本只有第一個攔截器,返回Promise,總是會造成請求二次重發,我后來請教官方,得知 resolve( request( url, options) ) 導致的重發請求,現在已經進行了修復,可以正常使用了,后台使用的node.js KOA,下面貼下代碼吧

/**** Token功能類 ****/
const jwt = require('jsonwebtoken');
/**** secret編碼 ****/
const secret = '002c6';
/*****
 * 過期時間延長24小時
 */
// const refreshTime = 86400000;
/*****
 * 過期時間延長10s
 */
const refreshTime = 10000;

class Token{
     constructor() {
        this.jwt = jwt;
        this.secret = secret;
    }
    /*******生成的格式就是{alg: "HS256",typ: "JWT"}.{name: "你的名字字段",refreshTime: 10000,iat: 1573704935,exp: 1573704947.[signature] */
    createToken( payload , dateStr ){
        payload.refreshTime = refreshTime;
        return this.jwt.sign(payload, this.secret, { expiresIn: dateStr });
    }
}

// module.exports = {
//   proToken : new Token()
// }

exports.proToken = new Token()

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM