当前位置: 首页 > article >正文

vue2 通过路由拦截实现 token 刷新功能

需求场景:系统当中设置的 token 过期时间为 1 小时,当用户在一小时内一直在操作系统,那么当即将一小时 token 快过期时,前端调用刷新 token 的接口,实现为用户的 token 刷新功能。

具体实现流程:

后端:

  1. 在 nacos 中配置 token 过期时间,及需要刷新 token 的时间
  2. 在网关层面,控制所有接口的响应头中添加 need-refresh 的状态标识
  3. 提供一个刷新 token 的接口

前端:

  1. token 已经过期:跳转到登录页面
  2. token 即将过期:在路由拦截器中,通过接口请求头中的 need-refresh 标识,判断调用刷新 token 接口的时机
  3. 特殊处理:为了防止同时进行多个 token 刷新请求,使用了 isRefreshing 标志来控制。只有当 isRefreshing 为 false 时,才会开始新的刷新流程。

前端代码实现:

import axios from 'axios';
import store from '@/store';
import router, { resetRouter } from '@/router';

const instance = axios.create({
  baseURL: '',
  timeout: 10000,
});

let isRefreshing = false;
let pendingRequests = [];

// 添加请求拦截器
instance.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem('token');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }

    // 检查 token 是否即将过期
    const needRefresh = config.headers['need-refresh'] == 'true';
    if (needRefresh) {
      return new Promise((resolve) => {
        if (!isRefreshing) {
          refreshToken().then((newToken) => {
            resolve({
              ...config,
              headers: {
                ...config.headers,
                Authorization: `Bearer ${newToken}`,
              },
            });
          });
        } else {
          // 如果已经在刷新token,则将请求加入队列
          addPendingRequest(config, resolve);
        }
      });
    }

    return config;
  },
  (error) => Promise.reject(error)
);

// token 过期
const tokenExpire = () => {
  // token 过期,清除本地缓存,跳转到登录页面
  store.dispatch('login/clearStorage');
  resetRouter();
  // 重置 state 中的值
  Object.keys(store.state).forEach((storeName) => {
    store.commit(`${storeName}/RESET_STATE`);
  });
  router.push('/login');
};

// 添加响应拦截器
instance.interceptors.response.use(
  (response) => {
    const needRefresh = response.headers['need-refresh'] == 'true';
    if (response.data && response.data.status == 401) {
      tokenExpire();
      return;
    }
    if (needRefresh) {
      return refreshToken().then(() => response);
    }
    return response;
  },
  async (error) => {
    const originalRequest = error.config;
    if (
      error.response &&
      error.response.status === 401 &&
      !originalRequest._retry
    ) {
      originalRequest._retry = true;
      try {
        await refreshToken();
        return instance(originalRequest);
      } catch (refreshError) {
        // 清空pendingRequests,因为 token 刷新失败
        pendingRequests = [];
        throw refreshError;
      }
    }
    return Promise.reject(error);
  }
);

async function refreshToken() {
  if (!isRefreshing) {
    isRefreshing = true;
    try {
      const response = await store.dispatch('login/refreshTokenReq');
      const { token } = response.data;
      localStorage.setItem('token', token);
      // 处理挂起的请求
      pendingRequests.forEach(({ config, resolve }) => {
        resolve({
          ...config,
          headers: { ...config.headers, Authorization: `Bearer ${token}` },
        });
      });
      pendingRequests = []; // 清空队列
    } catch (error) {
      console.error('Failed to refresh token', error);
      // 可能需要重定向到登录页面
      tokenExpire();
    } finally {
      isRefreshing = false;
    }
  }
}

function addPendingRequest(config, resolve) {
  pendingRequests.push({ config, resolve });
}

export default instance;


http://www.kler.cn/a/390580.html

相关文章:

  • C# 委托与匿名方法
  • Android 配置默认输入法
  • stringUtils详细解释
  • P8680 [蓝桥杯 2019 省 B] 特别数的和
  • Systemd: disable和mask的区别
  • javascript 函数【知识点整理】
  • 手持测温热像仪市场规模:预计2030年全球市场规模将达到24.9亿美元
  • 【Leecode】Leecode刷题之路第45天之跳跃游戏II
  • HARCT 2025 新增分论坛2:机器人系统智能控制
  • docker基础:搭建centos7(详见B站泷羽sec)
  • kafka夺命三十问——16-22问
  • 网络安全:挑战、策略与未来趋势
  • TensorRT基础知识
  • 什么是Stream流?
  • 【模块一】kubernetes容器编排进阶实战之k8s基础概念
  • Java 后端开发框架总结笔记:
  • Python 爬虫运行状态监控:进度、错误与完成情况
  • 智能零售:AI赋能电商行业的全面升级与高效运营
  • Spring Boot实战:编程训练系统开发手册
  • ssm+vue710的线上招聘问答系统的设计与实现
  • 云计算答案
  • 使用ThorUi
  • @SpringBootApplication源码解析
  • 【ComfyUI +BrushNet+PowerPaint】图像修复(根据题词填充目标)——ComfyUI-BrushNet
  • shodan7(泷羽sec)
  • SystemVerilog学习笔记(一):数据类型