1. 程式人生 > 其它 >令牌過期以後重新整理token並重調介面

令牌過期以後重新整理token並重調介面

技術標籤:前端vue

一、目標

設定客戶端時間,只要超過客戶端時間,系統自動退回到登入頁面;當未超過客戶端設定的時間時,呼叫介面,發現令牌過期了,先呼叫更新令牌介面,然後再重新呼叫介面。

我原來的token重新整理方式是:登入獲取過期時間,在每次呼叫介面的時候比較當前時間和過期時間,如果呼叫介面的時候發現即將過期,重新整理令牌獲取新的時間;如果呼叫介面發現過期了,退出頁面。由於是有相似之處的,所以我在此基礎上進行修改。

二、思路

1、在外接引數中設定客戶端時間

2、登入的時候儲存客戶端時間,每次呼叫介面的時候判斷有沒有過期

3、登入的時候獲取token令牌

4、每次呼叫介面,後端會進行判斷,如果過期,會報401,並在response headers中攜帶token-expired引數

5、如果獲取到這個引數,則進行令牌重新整理,並在重新整理以後再重新呼叫過期介面

三、實現

1、在config.js 中設定客戶端時間,此處設定7天過期

window.g = {
  EXPIRE_TIME: 7,//過期時間 7天
}

2、在登入的時候存客戶端過期時間

//登入的時候設定客戶端過期時間
let expires = Date.parse(new Date())/1000 + window.g.EXPIRE_TIME *24*3600 //換算成秒
setExpires(expires)

3、每次呼叫介面的時候對客戶端時間的判斷

以get為例:

  async get(url, data, query) {
    //get請求方式
    await expiresCalculation()
      .then(() => {
        //console.log("時間戳沒過期");
      })
      .catch(() => {
        //console.log("更新令牌~");
      });
    const options = getOptions(url, "get", data, query);
    return axios(options)
      .then(response => {
        return checkStatus(response);
      })
      .catch(res => {
        return checkCode(res);
      });
  },
import router from '@/router'
import store from '@/store'
import { getExpires } from '@/utils/auth'
const expiresCalculation = () => {
  const expires = getExpires() || 0;
  const nDate = new Date();
  const nTime = nDate.getTime();
  const nTimeSecond = Math.floor(nTime / 1000);
  const expTime = expires - nTimeSecond;

  return new Promise(async (resolve, reject) => {
    //客戶端時間過期(7天)就退出介面
    if (expTime && expTime > 0) {
      resolve()
    } else {
      await store.dispatch('user/logout')
      router.push('/login');
    }
  })
}

export default expiresCalculation;

4、呼叫介面,如果報401,並在response headers中攜帶token-expired引數,呼叫令牌重新整理介面,獲取新token儲存,並重新調介面

import {handleRefreshToken} from "@/api/http.js"

fetchData() {
     getExpertPages().then(async res => {
        if (!res) return
        if(res && res.code === 10000){
          this.patList = res.content.list
          this.totalCount = res.content.totalCount
        }else if(res.headers['token-expired']){  //token過期
          let resBack = await handleRefreshToken(res) //重新整理token並重新呼叫
          this.patList = resBack.content.list
          this.totalCount = resBack.content.totalCount
        }
      }).catch(() => {
        ...
      })
}

以get為例:

http.js:

async get(url, data, query) {
    //get請求方式
    await expiresCalculation()
      .then(() => {
        //console.log("時間戳沒過期");
      })
      .catch(() => {
        //console.log("更新令牌~");
      });
    const options = getOptions(url, "get", data, query);
    return axios(options)
      .then(response => {
        return checkStatus(response);
      })
      .catch(res => {
        return checkCode(res);
      });
  },
  function checkStatus(response) {
 //處理響應資料
  if (response && response.status === 200) {
    ...
  }else if(response.status === 401){
    return response;
  } else{
    ...
  }
export function handleRefreshToken(response){
  //如果能走到這一步,說明客戶端時間已經判斷過,再判斷下token
  if(response.headers['token-expired']){
    let data = {
      accessToken: getToken(), //訪問令牌
      refreshToken: getRefreshToken()//重新整理令牌
    }
    return new Promise((resolve, reject) => {
      refreshToken(data).then(async (res) => {
        if (res) {
          const cont = res.content;
          await store.dispatch('user/setNewTokenInfo', cont)
          //重新呼叫介面
         resolve(await repeatRequest(response))
        }else{
          console.log('token重新整理失敗')
          reject()
        }
      }).catch(error =>{
        reject(error)
      })
    })
   } else {
     console.log('客戶端時間未過期,token過期且無token-expired')
     return false
   }
} 
function repeatRequest(response){
  let dataUrl = `${response.config.baseURL}${response.config.url}`;
  // 二進位制流轉換為base64
  return axios[response.config.method](dataUrl, {
    params: response.config.params,
  }).then(res => {
    return checkStatus(res)
  }).catch(ex => {
     console.error(ex)
  });
}

四、測試

以上就算實現的程式碼思路了。

那要怎麼測試呢?登入獲取正常的令牌,然後用過期的令牌呼叫介面,然後重新整理令牌以後,需要用新的令牌再呼叫一次介面。所以,我這邊考慮在本地storage中加一個changToken的引數做判斷,登入以後changToken存入true。如果需要測試,delete這個changeToken引數。刪除以後,呼叫任意介面都會使用一個過期的令牌。重新整理令牌重新呼叫介面以後,引數重新變成true。

即:刪除changeToken引數 =〉呼叫過期token =〉呼叫過期token,changeToken引數變true =〉 changeToken引數變true,呼叫新token。

加入程式碼:

登入的時候:

window.localStorage.setItem('changeToken', true)

重新呼叫介面的時候:

function repeatRequest(response){
  window.localStorage.setItem('changeToken', true)
}

請求攔截的時候:

axios.interceptors.request.use(
  config => {
    // 請求攔截
    const isToken = (config.headers || {}).isToken === false;
    let token = store.getters.token
      ? store.getters.token
      : getToken()
        ? getToken()
        : "";
    if (token && !isToken) {
        //用changeToken判斷token呼叫
      if(window.localStorage.getItem('changeToken')) {
        config.headers["Authorization"] = "Bearer " + token 
      }
      else 
      config.headers["Authorization"] = '此處是過期token' 
      config.headers["sysappid"] = sysAppId;
    }
    return config;
  },
  error => {
    return Promise.reject(error);
  }
);

最終實現效果: