前端实现无感刷新 Token 使用 Axios

在现代的前端开发中,离我们最近的就是微信JSSDK,除此很多应用都需要使用 Access Token 来访问受保护的资源。为了保证安全性,Access Token 通常具有有限的有效期,这就需要使用 Refresh Token 来无感刷新 Access Token,保持用户会话的持续性。
本文将介绍如何利用 Axios 实现无感刷新 Token,使得用户在前端使用过程中不需要手动重新登录或者感知 token 的刷新。
1. 概述
在应用中,用户的登录会话通常包括 Access Token 和 Refresh Token。Access Token 用于访问受保护的 API,而 Refresh Token 用于在 Access Token 过期时,自动向服务器请求新的 Access Token。
为了实现无感刷新的机制,我们将利用 Axios 拦截器,自动检测 Access Token 是否过期并进行刷新操作。
2. 实现步骤
2.1 设计流程
用户第一次登录后,后端返回 Access Token 和 Refresh Token。 当用户发送 API 请求时,前端将 Access Token 附加在请求头中。 当 Access Token 过期时,使用 Refresh Token 请求新的 Access Token。 如果刷新成功,更新本地存储中的 Access Token,并重试之前的请求。 如果刷新失败(如 Refresh Token 过期),则用户需要重新登录。
2.2 前端代码实现
2.2.1 设置 Axios 实例
首先,我们需要创建一个 Axios 实例,并配置全局的请求拦截器和响应拦截器。
import axios from "axios";
import {
getAccessToken,
getRefreshToken,
setAccessToken,
removeTokens,
} from "./tokenUtils"; // 你需要实现这部分方法
// 创建 axios 实例
const axiosInstance = axios.create({
baseURL: "https://api.example.com",
timeout: 10000, // 设置请求超时时间
});
// 请求拦截器:为每个请求添加 Access Token
axiosInstance.interceptors.request.use(
(config) => {
const token = getAccessToken(); // 获取当前存储的 Access Token
if (token) {
config.headers["Authorization"] = `Bearer ${token}`; // 在请求头中添加 Access Token
}
return config;
},
(error) => Promise.reject(error),
);
// 响应拦截器:处理 token 刷新逻辑
axiosInstance.interceptors.response.use(
(response) => response, // 如果响应成功,直接返回数据
async (error) => {
const originalRequest = error.config;
// 判断是否因为 token 过期(假设 401 错误表示过期)
if (
error.response &&
error.response.status === 401 &&
!originalRequest._retry
) {
originalRequest._retry = true; // 防止重复刷新
try {
// 使用 Refresh Token 刷新 Access Token
const newAccessToken = await refreshToken();
setAccessToken(newAccessToken); // 更新本地的 Access Token
originalRequest.headers["Authorization"] = `Bearer ${newAccessToken}`; // 更新请求头中的 Access Token
return axiosInstance(originalRequest); // 重试原始请求
} catch (refreshError) {
// 如果刷新失败,则清除所有 token,并引导用户重新登录
removeTokens();
window.location.href = "/login"; // 重定向到登录页面
return Promise.reject(refreshError);
}
}
return Promise.reject(error);
},
);
// 刷新 Access Token
async function refreshToken() {
const refreshToken = getRefreshToken(); // 获取存储的 Refresh Token
const response = await axiosInstance.post("/auth/refresh", {
refresh_token: refreshToken,
});
return response.data.access_token; // 假设服务器返回新的 Access Token
}
export default axiosInstance;2.2.2 工具函数:获取和存储 Token
你需要实现一些方法来获取、存储和删除 Access Token 和 Refresh Token。通常这些 token 会存储在 localStorage 或 sessionStorage 中。
// tokenUtils.js
// 获取 Access Token
export function getAccessToken() {
return localStorage.getItem("access_token");
}
// 获取 Refresh Token
export function getRefreshToken() {
return localStorage.getItem("refresh_token");
}
// 设置 Access Token
export function setAccessToken(token) {
localStorage.setItem("access_token", token);
}
// 删除所有 token
export function removeTokens() {
localStorage.removeItem("access_token");
localStorage.removeItem("refresh_token");
}2.3 前端实现的关键点
请求拦截器:每次发送请求时,我们会在请求头中添加 Access Token,确保用户能够访问受保护的 API。 响应拦截器:当服务器返回 401 错误时(即 Access Token 过期),会自动触发刷新流程,使用 Refresh Token 请求新的 Access Token 并重试原始请求。 刷新逻辑:在刷新 Token 时,服务器会验证 Refresh Token 是否有效。如果有效,则返回新的 Access Token,如果无效,则清除所有 tokens 并引导用户重新登录。
处理跨域请求
如果你的后端 API 与前端应用部署在不同的域中,请确保在后端开启了 CORS(跨域资源共享),并且允许携带 cookie 或 Authorization Header。
错误处理
在进行 token 刷新时,可能会发生一些错误(如 Refresh Token 无效、网络请求失败等)。这些错误需要被合理处理,避免导致用户的登录会话丢失。上述代码中,如果刷新失败,用户会被重定向到登录页面。
总结
通过利用 Axios 拦截器,我们可以实现一个 无感刷新 Token 的机制,确保用户在 Access Token 过期 时,应用能够自动获取新的 Access Token,而不需要用户干预。这个过程是透明的,用户不会感知到任何登录状态的改变。
请求拦截器 用于为每个请求添加 Access Token。 响应拦截器 用于捕获 401 错误,并在 Access Token 过期时,使用 Refresh Token 刷新 token 并重试请求。 如果刷新失败,用户会被引导到登录页面,保证了安全性。 这种机制可以有效提高用户体验,避免频繁的重新登录操作,同时确保系统的安全性。