提高网站转化率,响应式网站解决方案,企业网站优化策略,手表网站建站摘要#xff1a; Axios 无感知刷新令牌是一种在前端应用中实现自动刷新访问令牌#xff08;access token#xff09;的技术#xff0c;确保用户在进行 API 请求时不会因为令牌过期而中断操作 目录概览 XMLHttpRequestAxiosFetch APIJQuni.request注意事项#xff1a; 访问… 摘要 Axios 无感知刷新令牌是一种在前端应用中实现自动刷新访问令牌access token的技术确保用户在进行 API 请求时不会因为令牌过期而中断操作 目录概览 XMLHttpRequestAxiosFetch APIJQuni.request注意事项 访问令牌Access Token用于访问受保护资源的凭证通常有一定的有效期。刷新令牌Refresh Token用于获取新的访问令牌当访问令牌过期时使用。
实现步骤
设置拦截器在 Axios的请求拦截器中添加逻辑检查当前时间与令牌的过期时间。如果访问令牌已过期但刷新令牌仍然有效则调用刷新令牌接口获取新的访问令牌。更新令牌存储一旦获得新的访问令牌将其存储到 localStorage、Vuex 或其他状态管理工具中以便后续请求使用新令牌。重试原始请求在成功刷新令牌后重新发送被拦截的请求此时使用新的访问令牌。
XMLHttpRequest
// 创建 XMLHttpRequest 实例
const xhr new XMLHttpRequest();// 登录成功后保存 Token 和 Refresh Token
function onLoginSuccess(response) {localStorage.setItem(accessToken, response.data.accessToken);localStorage.setItem(refreshToken, response.data.refreshToken);
}// 发起请求的函数
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 的函数
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; // 表示刷新失败}
}// 拦截响应并处理 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(); // 重新发送请求} else {alert(请重新登录); // Token 刷新失败可能需要用户重新登录}});}
});
Axios
import axios from axios;// 创建 Axios 实例
const apiClient axios.create({baseURL: https://your-api-url.com,// 其他配置...
});// 响应拦截器
apiClient.interceptors.response.use(response {return response;
}, error {const { response } error;if (response response.status 401) {return refreshToken().then(refreshed {if (refreshed) {// 令牌刷新成功重试原始请求return apiClient.request(error.config);} else {// 令牌刷新失败可能需要用户重新登录return Promise.reject(error);}});}return Promise.reject(error);
});// 令牌刷新函数
function refreshToken() {return apiClient.post(/path/to/refresh, {// 刷新令牌所需的参数例如 refresh_token}).then(response {if (response.data.success) {// 假设响应数据中包含新的访问令牌const newAccessToken response.data.newAccessToken;// 更新令牌存储localStorage.setItem(accessToken, newAccessToken);// 更新 Axios 实例的 headers以便后续请求使用新令牌apiClient.defaults.headers.common[Authorization] Bearer ${newAccessToken};return true; // 表示刷新成功} else {return false; // 表示刷新失败}});
}
Fetch API
// 定义一个函数来处理Fetch请求
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) { // 假设401表示令牌过期const refreshToken localStorage.getItem(refreshToken);if (!refreshToken) {throw new Error(No refresh token available);}// 调用刷新令牌接口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);// 重新尝试原始请求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函数 这是一个封装了Fetch API的函数它首先检查本地存储中的访问令牌是否存在并在请求头中添加该令牌。如果响应状态码为401表示令牌过期则尝试使用刷新令牌获取新的访问令牌并重新发送原始请求。刷新令牌逻辑 在检测到令牌过期时函数会调用刷新令牌接口并将新的访问令牌存储到本地存储中。然后它会重新设置请求头中的授权信息并重新发送原始请求。错误处理 如果在刷新令牌或发送请求的过程中发生错误函数会抛出相应的错误并在控制台中记录错误信息。
JQ
// 创建 JQuery 实例
const apiClient $.ajaxSetup({baseURL: https://your-api-url.com,// 其他配置...
});// 响应拦截器
$.ajaxSetup({complete: function(jqXHR, textStatus) {if (textStatus error jqXHR.status 401) {return refreshToken().then(refreshed {if (refreshed) {// 令牌刷新成功重试原始请求return apiClient.request(this);} else {// 令牌刷新失败可能需要用户重新登录alert(请重新登录);}});}}
});// 令牌刷新函数
function refreshToken() {return $.ajax({url: /path/to/refresh,method: POST,data: {refresh_token: localStorage.getItem(refreshToken)},dataType: json}).then(response {if (response.data.success) {// 假设响应数据中包含新的访问令牌const newAccessToken response.data.newAccessToken;// 更新令牌存储localStorage.setItem(accessToken, newAccessToken);// 更新 JQuery 实例的 headers以便后续请求使用新令牌apiClient.defaults.headers.common[Authorization] Bearer ${newAccessToken};return true; // 表示刷新成功} else {return false; // 表示刷新失败}});
}
uni.request
// 导入封装的request插件
import http from ./interface;
import { getRefreshToken } from /common/api/apis.js; // 刷新token接口let isRefreshing false; // 是否处于刷新token状态中
let fetchApis []; // 失效后同时发送请求的容器
let refreshCount 0; // 限制无感刷新的最大次数function onFetch(newToken) {refreshCount 1;if (refreshCount 3) {refreshCount 0;fetchApis [];return Promise.reject();}fetchApis.forEach(callback {callback(newToken);});// 清空缓存接口fetchApis [];return Promise.resolve();
}// 响应拦截器
http.interceptor.response((response) {if (response.config.loading) {uni.hideLoading();}// 请求成功但接口返回的错误处理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 Promisereturn 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 {// 超过循环次数时回到登录页这里可以添加你执行退出登录的逻辑uni.showToast({ title: 登录失效请重新登录, icon: error });setTimeout(() {uni.reLaunch({ url: /pages/login/login });}, 1500);});}).catch(err {// refreshToken接口报错证明refreshToken也过期了那没办法啦重新登录呗uni.showToast({ title: 登录失效请重新登录, icon: error });setTimeout(() {uni.reLaunch({ url: /pages/login/login });}, 1500);}).finally(() { isRefreshing false });}return new Promise((resolve) { // 此处的promise很关键就是确保你的接口返回值在此处resolve以便后续代码执行addFetchApi((newToken) {error.config.header[Authorization] Bearer ${newToken};http.request(error.config).then(response {resolve(response);});});});}break;default:break;}
});
注意事项
错误处理确保在刷新令牌失败时有适当的错误处理机制例如提示用户重新登录。并发请求处理多个请求同时需要刷新令牌的情况避免重复刷新。安全性确保刷新令牌的安全存储和传输防止被恶意攻击者获取。