You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何封装通用Fetch函数统一处理JWT认证与401重定向?

通用Fetch封装方案

当然可以通过封装通用Fetch函数来统一处理状态码检查、401重定向以及token自动刷新逻辑,避免每个请求重复写相同的判断代码。以下是适配你服务端JWT认证逻辑的实现方案:

封装通用Fetch函数

// 可将该函数放在独立工具文件中,比如src/utils/api.js
let accessToken = localStorage.getItem('accessToken') || '';

const apiFetch = async (url, options = {}) => {
  // 统一设置请求头,携带当前有效accessToken
  const headers = {
    'Content-Type': 'application/json',
    ...options.headers,
    ...(accessToken && { Authorization: accessToken })
  };

  const config = {
    ...options,
    headers,
    credentials: 'include' // 确保请求携带cookie(refreshToken存储在cookie中)
  };

  let response = await fetch(url, config);

  // 处理401未授权状态
  if (response.status === 401) {
    // 调用刷新token接口尝试获取新的accessToken
    const refreshResponse = await fetch('/api/refresh-token', {
      method: 'POST',
      credentials: 'include'
    });

    if (refreshResponse.ok) {
      const newToken = refreshResponse.headers.get('Authorization');
      await refreshResponse.json();
      
      // 更新本地存储的accessToken
      accessToken = newToken;
      localStorage.setItem('accessToken', newToken);

      // 用新token重新发起原请求
      config.headers.Authorization = newToken;
      response = await fetch(url, config);
    } else {
      // refreshToken也失效,直接重定向到登录页
      window.location.href = '/login';
      return Promise.reject(new Error('登录已过期,请重新登录'));
    }
  }

  // 统一处理非成功状态码
  if (!response.ok) {
    const errorData = await response.json().catch(() => ({ message: '请求失败' }));
    return Promise.reject(new Error(errorData.message || `请求错误:${response.status}`));
  }

  return response.json();
};

关键逻辑说明

  • 全局token管理:用变量accessToken维护当前有效令牌,更新后所有后续请求会自动使用新token
  • 自动重试机制:遇到401时自动调用刷新token接口,获取新token后重新发起原请求,对业务代码完全透明
  • 统一错误捕获:所有非2xx状态码都会被封装为错误抛出,业务代码只需通过try/catch处理异常
  • 凭证携带:设置credentials: 'include'确保请求携带cookie,让服务端能读取到refreshToken

业务代码中使用

直接用封装好的apiFetch替换原生fetch,无需再手动处理401:

// 原写法
// const res = await fetch('/api/user');
// const d = await res.json();
// if(res.status === 401) { redirect('/login') }

// 新写法
try {
  const userData = await apiFetch('/api/user');
  // 处理业务数据
} catch (error) {
  // 处理其他错误(如网络异常、服务器500等)
  console.error(error.message);
}

服务端适配建议

你现有的authenticate中间件可以拆分出单独的刷新token接口,方便客户端调用:

// 新增refresh-token接口
app.post('/api/refresh-token', (req, res) => {
  const refreshToken = req.cookies['refreshToken'];
  if (!refreshToken) {
    return res.status(401).send('No refresh token provided.');
  }

  try {
    const decoded = jwt.verify(refreshToken, secretKey);
    const newAccessToken = jwt.sign({ user: decoded.user }, secretKey, { expiresIn: '1h' });
    res
      .cookie('refreshToken', refreshToken, { httpOnly: true, sameSite: 'strict' })
      .header('Authorization', newAccessToken)
      .json(decoded.user);
  } catch (error) {
    return res.status(400).send('Invalid refresh token.');
  }
});

这样就实现了请求逻辑的统一处理,无需在每个请求中重复编写401判断和重定向代码。

内容的提问来源于stack exchange,提问作者wepro01

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 18:13:14