Skip to content

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

alt text

在现代的前端开发中,离我们最近的就是微信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 实例,并配置全局的请求拦截器和响应拦截器。

js
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 中。

js
// 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 并重试请求。 如果刷新失败,用户会被引导到登录页面,保证了安全性。 这种机制可以有效提高用户体验,避免频繁的重新登录操作,同时确保系统的安全性。

上次更新于: