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

异步函数运行时Auth Token定时更新失效致403错误求助

问题排查思路及解决方案

核心问题分析

从代码来看,导致1小时后请求返回403的主要原因集中在token变量的作用域/更新逻辑、异步操作的竞态条件、闭包捕获旧token这几个点上,下面逐一拆解排查思路和修复方案:


排查思路

  1. 验证token变量的作用域

    • 检查token是否为全局/模块级变量:如果token是authFetch内部的局部变量,fetchSubmodels根本无法获取到更新后的值。
    • 查看authFetch中的日志输出:确认inside token var是否打印出最新的token,以及外部token变量是否同步更新。
  2. 确认定时器执行状态

    • 检查日志中是否定期出现------------------Auth Updated------------------:如果没有,说明setInterval没有正常触发,可能是代码执行流程中出现了阻塞或错误。
    • 注意clearInterval(authFetch)是无效代码:clearInterval需要传入定时器ID,而非函数本身,这行代码不会清除定时器,但可能不会直接导致token不更新。
  3. 检查异步操作的竞态问题

    • setInterval调用异步函数authFetch时,不会等待前一次请求完成就会触发下一次,可能导致多次并发的认证请求,进而导致token更新混乱。
    • fetchSubmodels中使用forEach(async () => {}):forEach不会等待异步回调完成,且回调会闭包捕获初始的token值,即使全局token更新,后续请求仍会使用旧token。
  4. 验证token赋值逻辑

    • token = await accessToken是冗余代码:accessToken是同步获取的响应数据,不需要await,这可能导致赋值延迟或意外问题。
    • 数据库查询的默认值错误:TokenFromDB[0] || [{ accessToken: null, time: 0 }]中,默认值是数组而非对象,解构时会出错,导致token无法正确初始化。

解决方案

1. 修正token变量作用域与赋值逻辑

首先确保token是全局/模块级变量,同时修复赋值和数据库查询的错误:

// 在代码顶部声明全局token变量
let token = null;

const authFetch = async () => {
  const authCollection = client.db("WheelPros_data").collection("auth");
  // 用findOne替代find().toArray(),更高效且直接获取单条文档
  const tokenDoc = await authCollection.findOne({ _id: 1 });
  // 修复默认值为对象,避免解构错误
  const { accessToken: dbToken, time: lastFetchedAuth } = tokenDoc || { accessToken: null, time: 0 };

  const axiosConfig = {
    headers: {
      "Content-Type": "application/json",
      accept: "application/json",
    },
  };

  try {
    const res = await axios.post(
      `${process.env.PROD_API_URL}/auth/v1/authorize`,
      {
        userName: process.env.WHEELPROS_USERNAME,
        password: process.env.PASSWORD,
      },
      axiosConfig
    );

    const { accessToken } = res.data;
    await authCollection.updateOne(
      { _id: 1 },
      {
        $set: {
          accessToken: accessToken,
          time: new Date().getTime() / 1000,
          Date: new Date(),
        },
      },
      { upsert: true }
    );

    // 直接赋值,去掉多余的await
    token = accessToken;
    console.log("newAccessToken : ", accessToken);
    console.log("inside token var = ", token);
    console.log("------------------Auth Updated------------------");
  } catch (err) {
    console.error("AXIOS ERROR: ", err);
  }
};

// 初始化token
await authFetch();

2. 用递归setTimeout替代setInterval,避免异步竞态

setInterval会无视异步函数的执行状态,改用递归setTimeout确保前一次认证请求完成后再触发下一次:

// 保存定时器ID,方便后续清除
let authRefreshTimer;

const scheduleAuthRefresh = async () => {
  try {
    await authFetch();
  } catch (err) {
    console.error("Failed to refresh auth token:", err);
  }
  // 3590000ms = 59分50秒,提前10秒刷新token
  authRefreshTimer = setTimeout(scheduleAuthRefresh, 3590000);
};

// 启动定时刷新
scheduleAuthRefresh();

// 如需清除定时器,调用:
// clearTimeout(authRefreshTimer);

3. 修复fetchSubmodels中的异步处理与token引用问题

forEach无法处理异步循环,改用for...of确保每次请求都等待完成,且每次请求前都会获取最新的token值:

const fetchSubmodels = async () => {
  const modelDataFromDB = await collection.find({ modelData: { $exists: true } }).toArray();
  console.log(modelDataFromDB.length);

  // 用for...of替代forEach,确保异步操作顺序执行且获取最新token
  for (const modeldata of modelDataFromDB) {
    const dataSearch = modeldata.modelData.split(';');
    try {
      const { data } = await axios.get(
        `${process.env.PROD_API_URL}/${dataSearch[0]}/makes/${dataSearch[1]}/models/${dataSearch[2]}/submodels`,
        {
          headers: {
            Authorization: `Bearer ${token}`,
            "Content-Type": "application/json",
          },
        }
      );

      await collection.updateOne(
        { modelData: modeldata.modelData },
        { $set: { submodels: data } }
      );
      console.log(`pushing the submodels to ${modeldata.modelData} `);
    } catch (error) {
      console.error(`Failed to fetch submodels for ${modeldata.modelData}:`, error);
    }
  }
};

await fetchSubmodels();

4. 增加token有效性检查(可选优化)

在authFetch中先检查token是否仍在有效期内,避免不必要的认证请求:

const authFetch = async () => {
  const authCollection = client.db("WheelPros_data").collection("auth");
  const tokenDoc = await authCollection.findOne({ _id: 1 });
  const now = Date.now() / 1000;
  // 假设token有效期为1小时(3600秒),提前100秒刷新
  const validDuration = 3500;

  if (tokenDoc && (now - tokenDoc.time) < validDuration) {
    token = tokenDoc.accessToken;
    console.log("Using existing valid token");
    return;
  }

  // 以下是原有的请求新token逻辑...
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 20:25:30