欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

前端無感刷新token的實(shí)現(xiàn)步驟

 更新時(shí)間:2024年11月23日 15:14:01   作者:我就不思  
這篇文章主要給大家介紹了關(guān)于前端無感刷新token的實(shí)現(xiàn)步驟,Axios無感知刷新令牌技術(shù)通過設(shè)置請(qǐng)求攔截器和刷新邏輯,確保API請(qǐng)求不會(huì)因令牌過期而中斷,使用訪問令牌和刷新令牌實(shí)現(xiàn)自動(dòng)刷新,需要的朋友可以參考下

Axios 無感知刷新令牌是一種在前端應(yīng)用中實(shí)現(xiàn)自動(dòng)刷新訪問令牌(access token)的技術(shù),確保用戶在進(jìn)行 API 請(qǐng)求時(shí)不會(huì)因?yàn)榱钆七^期而中斷操作

  • 訪問令牌(Access Token):用于訪問受保護(hù)資源的憑證,通常有一定的有效期。
  • 刷新令牌(Refresh Token):用于獲取新的訪問令牌,當(dāng)訪問令牌過期時(shí)使用。

實(shí)現(xiàn)步驟:

  • 設(shè)置攔截器:在 Axios的請(qǐng)求攔截器中添加邏輯,檢查當(dāng)前時(shí)間與令牌的過期時(shí)間。如果訪問令牌已過期但刷新令牌仍然有效,則調(diào)用刷新令牌接口獲取新的訪問令牌。
  • 更新令牌存儲(chǔ):一旦獲得新的訪問令牌,將其存儲(chǔ)到 localStorage、Vuex 或其他狀態(tài)管理工具中,以便后續(xù)請(qǐng)求使用新令牌。
  • 重試原始請(qǐng)求:在成功刷新令牌后,重新發(fā)送被攔截的請(qǐng)求,此時(shí)使用新的訪問令牌。

XMLHttpRequest

// 創(chuàng)建 XMLHttpRequest 實(shí)例
const xhr = new XMLHttpRequest();

// 登錄成功后保存 Token 和 Refresh Token
function onLoginSuccess(response) {
    localStorage.setItem('accessToken', response.data.accessToken);
    localStorage.setItem('refreshToken', response.data.refreshToken);
}

// 發(fā)起請(qǐng)求的函數(shù)
function sendRequest(url, method, data) {
    return new Promise((resolve, reject) => {
        xhr.open(method, url);
        xhr.setRequestHeader('Authorization', `Bearer ${localStorage.getItem('accessToken')}`);
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    resolve(JSON.parse(xhr.responseText));
                } else {
                    reject({ status: xhr.status, response: xhr.responseText });
                }
            }
        };
        if (method === 'POST' && data) {
            xhr.send(JSON.stringify(data));
        } else {
            xhr.send();
        }
    });
}

// 刷新 Token 的函數(shù)
async function refreshToken() {
    const refreshToken = localStorage.getItem('refreshToken');
    const response = await fetch('/path/to/refresh', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({ refresh_token: refreshToken }),
    });
    const res = await response.json();
    if (res.success) {
        localStorage.setItem('accessToken', res.data.newAccessToken);
        return true; // 表示刷新成功
    } else {
        return false; // 表示刷新失敗
    }
}

// 攔截響應(yīng)并處理 Token 刷新
xhr.addEventListener('readystatechange', function() {
    if (xhr.readyState === 4 && xhr.status === 401) {
        refreshToken().then(refreshed => {
            if (refreshed) {
                xhr.setRequestHeader('Authorization', `Bearer ${localStorage.getItem('accessToken')}`);
                xhr.send(); // 重新發(fā)送請(qǐng)求
            } else {
                alert('請(qǐng)重新登錄'); // Token 刷新失敗,可能需要用戶重新登錄
            }
        });
    }
});

Axios

import axios from 'axios';

// 創(chuàng)建 Axios 實(shí)例
const apiClient = axios.create({
  baseURL: 'https://your-api-url.com',
  // 其他配置...
});

// 響應(yīng)攔截器
apiClient.interceptors.response.use(response => {
  return response;
}, error => {
  const { response } = error;
  if (response && response.status === 401) {
    return refreshToken().then(refreshed => {
      if (refreshed) {
        // 令牌刷新成功,重試原始請(qǐng)求
        return apiClient.request(error.config);
      } else {
        // 令牌刷新失敗,可能需要用戶重新登錄
        return Promise.reject(error);
      }
    });
  }
  return Promise.reject(error);
});

// 令牌刷新函數(shù)
function refreshToken() {
  return apiClient.post('/path/to/refresh', {
    // 刷新令牌所需的參數(shù),例如 refresh_token
  }).then(response => {
    if (response.data.success) {
      // 假設(shè)響應(yīng)數(shù)據(jù)中包含新的訪問令牌
      const newAccessToken = response.data.newAccessToken;
      // 更新令牌存儲(chǔ)
      localStorage.setItem('accessToken', newAccessToken);
      // 更新 Axios 實(shí)例的 headers,以便后續(xù)請(qǐng)求使用新令牌
      apiClient.defaults.headers.common['Authorization'] = `Bearer ${newAccessToken}`;
      return true; // 表示刷新成功
    } else {
      return false; // 表示刷新失敗
    }
  });
}

Fetch API

// 定義一個(gè)函數(shù)來處理Fetch請(qǐng)求
async function fetchWithToken(url, options = {}) {
    const token = localStorage.getItem('token');
    if (token) {
        options.headers = {
            ...options.headers,
            'Authorization': `Bearer ${token}`
        };
    }

    try {
        const response = await fetch(url, options);
        if (response.status === 401) { // 假設(shè)401表示令牌過期
            const refreshToken = localStorage.getItem('refreshToken');
            if (!refreshToken) {
                throw new Error('No refresh token available');
            }

            // 調(diào)用刷新令牌接口
            const refreshResponse = await fetch('/api/refresh-token', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ refreshToken })
            });

            if (refreshResponse.ok) {
                const data = await refreshResponse.json();
                localStorage.setItem('token', data.newAccessToken);
                // 重新嘗試原始請(qǐng)求
                options.headers['Authorization'] = `Bearer ${data.newAccessToken}`;
                return fetch(url, options);
            } else {
                throw new Error('Failed to refresh token');
            }
        }
        return response;
    } catch (error) {
        console.error('Fetch error:', error);
        throw error;
    }
}

// 使用示例
fetchWithToken('/api/protected-resource')
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error('Error:', error));
  • fetchWithToken函數(shù): 這是一個(gè)封裝了Fetch API的函數(shù),它首先檢查本地存儲(chǔ)中的訪問令牌是否存在,并在請(qǐng)求頭中添加該令牌。如果響應(yīng)狀態(tài)碼為401(表示令牌過期),則嘗試使用刷新令牌獲取新的訪問令牌,并重新發(fā)送原始請(qǐng)求。
  • 刷新令牌邏輯: 在檢測到令牌過期時(shí),函數(shù)會(huì)調(diào)用刷新令牌接口,并將新的訪問令牌存儲(chǔ)到本地存儲(chǔ)中。然后,它會(huì)重新設(shè)置請(qǐng)求頭中的授權(quán)信息,并重新發(fā)送原始請(qǐng)求。
  • 錯(cuò)誤處理: 如果在刷新令牌或發(fā)送請(qǐng)求的過程中發(fā)生錯(cuò)誤,函數(shù)會(huì)拋出相應(yīng)的錯(cuò)誤,并在控制臺(tái)中記錄錯(cuò)誤信息。

JQ

// 創(chuàng)建 JQuery 實(shí)例
const apiClient = $.ajaxSetup({
  baseURL: 'https://your-api-url.com',
  // 其他配置...
});

// 響應(yīng)攔截器
$.ajaxSetup({
  complete: function(jqXHR, textStatus) {
    if (textStatus === 'error' && jqXHR.status === 401) {
      return refreshToken().then(refreshed => {
        if (refreshed) {
          // 令牌刷新成功,重試原始請(qǐng)求
          return apiClient.request(this);
        } else {
          // 令牌刷新失敗,可能需要用戶重新登錄
          alert('請(qǐng)重新登錄');
        }
      });
    }
  }
});

// 令牌刷新函數(shù)
function refreshToken() {
  return $.ajax({
    url: '/path/to/refresh',
    method: 'POST',
    data: {
      refresh_token: localStorage.getItem('refreshToken')
    },
    dataType: 'json'
  }).then(response => {
    if (response.data.success) {
      // 假設(shè)響應(yīng)數(shù)據(jù)中包含新的訪問令牌
      const newAccessToken = response.data.newAccessToken;
      // 更新令牌存儲(chǔ)
      localStorage.setItem('accessToken', newAccessToken);
      // 更新 JQuery 實(shí)例的 headers,以便后續(xù)請(qǐng)求使用新令牌
      apiClient.defaults.headers.common['Authorization'] = `Bearer ${newAccessToken}`;
      return true; // 表示刷新成功
    } else {
      return false; // 表示刷新失敗
    }
  });
}

uni.request

// 導(dǎo)入封裝的request插件
import http from './interface';
import { getRefreshToken } from '@/common/api/apis.js'; // 刷新token接口

let isRefreshing = false; // 是否處于刷新token狀態(tài)中
let fetchApis = []; // 失效后同時(shí)發(fā)送請(qǐng)求的容器
let refreshCount = 0; // 限制無感刷新的最大次數(shù)

function onFetch(newToken) {
  refreshCount += 1;
  if (refreshCount === 3) {
    refreshCount = 0;
    fetchApis = [];
    return Promise.reject();
  }
  fetchApis.forEach(callback => {
    callback(newToken);
  });
  // 清空緩存接口
  fetchApis = [];
  return Promise.resolve();
}

// 響應(yīng)攔截器
http.interceptor.response((response) => {
  if (response.config.loading) {
    uni.hideLoading();
  }
  // 請(qǐng)求成功但接口返回的錯(cuò)誤處理
  if (response.data.statusCode && +response.data.statusCode !== 200) {
    if (!response.config.needPromise) {
      console.log('error', response);
      uni.showModal({
        title: '提示',
        content: response.data.message,
        showCancel: false,
        confirmText: '知道了'
      });
      // 中斷
      return new Promise(() => {});
    } else {
      // reject Promise
      return Promise.reject(response.data);
    }
  }
  return response;
}, (error) => {
  const token = uni.getStorageSync('token');
  const refreshToken = uni.getStorageSync('refreshToken');
  // DESC: 不需要做無感刷新的白名單接口
  const whiteFetchApi = ['/dealersystem/jwtLogin', '/dealersystem/smsLogin', '/sso2/login', '/dealersystem/isLogin'];
  switch (error.statusCode) {
    case 401:
    case 402:
      if (token && !whiteFetchApi.includes(error.config.url)) {
        if (!isRefreshing) {
          isRefreshing = true;
          getRefreshToken({ refreshToken }).then(res => {
            let newToken = res.data;
            onTokenFetched(newToken).then(res => {}).catch(err => {
              // 超過循環(huán)次數(shù)時(shí),回到登錄頁,這里可以添加你執(zhí)行退出登錄的邏輯
              uni.showToast({ title: '登錄失效,請(qǐng)重新登錄', icon: 'error' });
              setTimeout(() => {
                uni.reLaunch({ url: '/pages/login/login' });
              }, 1500);
            });
          }).catch(err => {
            // refreshToken接口報(bào)錯(cuò),證明refreshToken也過期了,那沒辦法啦重新登錄唄
            uni.showToast({ title: '登錄失效,請(qǐng)重新登錄', icon: 'error' });
            setTimeout(() => {
              uni.reLaunch({ url: '/pages/login/login' });
            }, 1500);
          }).finally(() => { isRefreshing = false });
        }
        return new Promise((resolve) => { // 此處的promise很關(guān)鍵,就是確保你的接口返回值在此處resolve,以便后續(xù)代碼執(zhí)行
          addFetchApi((newToken) => {
            error.config.header['Authorization'] = `Bearer ${newToken}`;
            http.request(error.config).then(response => {
              resolve(response);
            });
          });
        });
      }
      break;
    default:
      break;
  }
});

注意事項(xiàng):

  • 錯(cuò)誤處理:確保在刷新令牌失敗時(shí),有適當(dāng)?shù)腻e(cuò)誤處理機(jī)制,例如提示用戶重新登錄。
  • 并發(fā)請(qǐng)求:處理多個(gè)請(qǐng)求同時(shí)需要刷新令牌的情況,避免重復(fù)刷新。
  • 安全性:確保刷新令牌的安全存儲(chǔ)和傳輸,防止被惡意攻擊者獲取。

總結(jié) 

到此這篇關(guān)于前端無感刷新token的文章就介紹到這了,更多相關(guān)前端無感刷新token內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論