前端无感刷新token的实现步骤 |
实现步骤:
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 刷新失败,可能需要用户重新登录 } }); } }); Axiosimport 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));
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 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 => { // 超过循环次数时,回到登录页,这里可以添加你执行退出登录的逻辑 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; } }); 注意事项:
总结到此这篇关于前端无感刷新token的文章就介绍到这了,更多相关前端无感刷新token内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持! |