赞
踩
现在的网站基本会设置授权访问,对于某些资源的访问,需要有权限才能访问或者操作,而服务端判断用户是否有权访问或者操作,一般是通过Token实现,而Token一般会设置过期时间,对于关于授权方面的技术这里不会具体描述,这篇主要针对的是用户访问授权资源的时候,Token过期了,如何实现无需再次登录,无痛刷新Token,重新请求。以下是几种可实现的方法
在前端实现刷新 token(access token)的方法有多种,每种方法都有其优点和缺点。以下是几种常见的方法及其评估:
使用 setTimeout
或 setInterval
定时在 token 过期前一定时间内自动发起刷新请求。
let refreshTokenTimeout; const setRefreshTokenTimer = (expiresIn) => { // 设置定时器,在 token 过期前 5 分钟刷新 const refreshTime = expiresIn - 300000; // 5分钟 refreshTokenTimeout = setTimeout(refreshToken, refreshTime); } const refreshToken = async () => { try { const response = await fetch('/refresh-token'); const data = await response.json(); // 假设返回数据包含新的 access token 和它的过期时间(expiresIn) localStorage.setItem('refreshToken', data.refreshToken); setRefreshTokenTimer(data.expiresIn); } catch (error) { console.error("Failed to refresh token", error); // 可以根据需要处理错误,例如跳转到登录页面 } } // 初次设置定时器,这种需要后台返回token的过期时间,或者用户登录的时候获取token的时候同时存储当前的时间于localStorage setRefreshTokenTimer(initialExpiresIn);
在每个 API 请求之前检查 token 是否即将过期,如果即将过期,则首先刷新 token,然后再继续发送请求。
// 使用 Axios 作为示例请求库 import axios from 'axios'; const http= axios.create({ baseURL: '/api', timeout: 10000, }); http.interceptors.request.use(async (config) => { const token = localStorage.getItem('refreshToken'); const expiryTime = localStorage.getItem('tokenExpiryTime'); if (expiryTime && Date.now() >= expiryTime - 300000) { // 提前5分钟刷新 const newTokenData = await refreshToken(); config.headers['Authorization'] = `Bearer ${newTokenData.refreshToken}`; } else { config.headers['Authorization'] = `Bearer ${token}`; } return config; }, (error) => { return Promise.reject(error); }); const refreshToken = async () => { try { const response = await apiClient.post('/refresh-token'); const data = await response.data; localStorage.setItem('refreshToken', data.refreshToken); localStorage.setItem('tokenExpiryTime', Date.now() + data.expiresIn); return data; } catch (error) { console.error("Failed to refresh token", error); throw error; } }; // 在需要时使用 apiClient 发出请求 http.get('/some-protected-endpoint').then(response => { console.log(response.data); });
复杂度增加:需要处理并发刷新请求的问题,同一时刻多个请求可能触发多次刷新。
比如,getApi1,getApi2同时并发请求,都会经过Token过期判断
,都会发情token刷新请求,对于页面并发请求,比如多文件上传之类的并发,如果并发6条数据,则会请求6次token的刷新。
额外延迟:首次请求可能因为 token 刷新而略有延迟。
当 API 请求返回 401 Unauthorized 错误时,尝试刷新 token 并重新发送原始请求。
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios'; const http: AxiosInstance = axios.create({ baseURL: '/baseapi', //Mock timeout: 10000, }); const openTokenRefresh = false; // 是否开启token刷新,如果开启,就设置为true就好 const pendingRequests: ((token: string) => void)[] = []; // 保存所有请求的回调,用于刷新token后重新请求 const onTokenRefreshed = (token: string):void => { // 刷新token后,重新请求, pendingRequests.forEach((callback) => { callback(token); }); pendingRequests.length = 0; }; const addPendingRequest = (callback: (token: string) => void) => { pendingRequests.push(callback); }; //是否正在刷新token let isRefreshing = false; http.interceptors.response.use((response: AxiosResponse<any>) => response, async (error) => { if (error.response && error.response.status === 401 && openTokenRefresh) { //获取refreshToken const refreshToken = localStorage.getItem('refreshToken'); if (!refreshToken) { //没有refreshToken,跳转登录页面 window.location.href('/login') return Promise.reject('登录失效,请重新登录'); } if (!isRefreshing) { try{ isRefreshing = true; //请求更新token const data= await updataToken(refreshToken); localStorage.setItem('token', data.token); localStorage.setItem('refreshToken', data.refreshToken); onTokenRefreshed(data.token); }catch(err){ window.location.href('/login') return Promise.reject(err) } finally{ isRefreshing = false } } //存储当前请求 return new Promise<AxiosResponse<any>>((resolve) => { addPendingRequest((newToken: string) => { response.config.headers['Authorization'] ='Bearer'+ newToken; resolve(http(response.config)); }); }); } return Promise.reject(error); });
方法 | 优点 | 缺点 |
---|---|---|
基于定时器的自动刷新 | 简单易实现,自动化管理 | 不适用于后台刷新,可能导致不必要的网络请求 |
基于请求拦截器的刷新 | 高效,适用于后台刷新,减少不必要的网络请求, | 复杂度增加,需要处理并发刷新问题,同一时刻多个请求可能触发多次刷新。首次请求可能延迟 |
基于响应拦截器的刷新 | 只有在必要时才刷新 token,避免不必要的请求,能够处理 token 失效后的各种情况,包括并发问题。 | 首次请求失败可能增加延迟 |
选择哪种方法取决于你的具体需求和系统架构,通常基于请求拦截器和响应拦截器的方法更为普遍,因为它们能够更好地应对复杂的实际场景。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。