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

Node.js应用中新JWT刷新令牌验证失败的问题排查

问题分析与修复方案

看起来你的核心问题是新生成的刷新令牌无法通过验证,结合你的代码和错误信息,我找出了几个关键的逻辑错误,下面一步步帮你解决:

1. 新刷新令牌的Payload字段错误(最直接原因)

在regenrateToken函数中,你生成新refresh token时误用了req.owner._id,但解码后的refresh token payload里的用户ID字段是userId(看登录时的代码:jwt.sign({userId: user._id,...}))。这个错误会导致新refresh token的payload中userId为undefined,后续验证时调用decoded.userId.toString()会抛出TypeError,直接进入catch块返回"Your session is not valid"。

修复代码:

const regenrateToken = async (req, res)=>{
  const token = jwt.sign(
    {userId: req.owner.userId, username:req.owner.username, role: req.owner.role}, 
    process.env.JWT_SECRET, 
    { expiresIn: process.env.JWT_LIFETIME}
  );
  // 修正字段为req.owner.userId
  const refresh_Token = jwt.sign(
    {userId: req.owner.userId, username:req.owner.username, role: req.owner.role}, 
    process.env.Refresh_Secret
  );
  await redis_client.set(req.owner.userId.toString(), JSON.stringify(refresh_Token))
  // 把错误的401状态码改成200,成功生成令牌需返回正确状态
  res.status(200).json({data: {token, refresh_Token}})
};

2. Redis操作混用回调与Async/Await,逻辑顺序混乱

在refreshTokenverify中,你同时用了await和回调函数调用redis_client.get,这会导致next()在Redis查询完成前就执行,后续逻辑根本拿不到正确的Redis数据。而且验证token的逻辑也写错了——你存储的是token本身的JSON字符串,却尝试访问refresh_Token属性。

修复后的refreshTokenverify:

const refreshTokenverify = async (req, res, next) =>{
  const authHeader = req.headers.authorization;
  if(authHeader){
    const token = authHeader.split(" ")[1];
    if(!token) return res.status(401).json({status: false, message: "No token found"});
    try{
      const decoded = jwt.verify(token, process.env.Refresh_Secret);
      req.owner = decoded;
      
      // 改用async/await获取Redis数据,避免回调嵌套
      const storedTokenStr = await redis_client.get(decoded.userId.toString());
      if(storedTokenStr === null) return res.status(401).json({message: "Token not found in database"});
      
      // 解析存储的token字符串,直接和传入的token比较
      const storedToken = JSON.parse(storedTokenStr);
      if(storedToken !== token) return res.status(401).json({message: "Invalid request"});
      
      next();
    }catch(error){
      // 打印错误方便调试,快速定位是JWT验证/Redis连接/字段问题
      console.error("Refresh token verify error:", error);
      return res.status(401).json({message: "Your session is not valid"})
    }
  } else{
    res.status(401).json("You're not authenticated")
  }
}

3. 登录时的Redis操作异步问题

登录函数中,你用await+回调的方式操作Redis,可能导致token还没存入Redis就给用户返回了响应。建议统一用async/await风格:

修复登录函数中的Redis部分:

// 替换原来的Redis操作代码
const existingToken = await client.get(user._id.toString());
// 直接覆盖旧的refresh token,实现旧令牌失效的需求
await client.set(user._id.toString(), JSON.stringify(refresh_Token));

4. 额外优化建议

  • 给Redis中的refresh token设置过期时间:比如和refresh token本身的过期时间一致(如果有的话),避免Redis中堆积无效令牌。
  • 保留错误日志:在catch块中打印错误信息,能快速定位问题根源,减少调试时间。

完成以上修复后,你的流程就能正常工作了:用户登录得到令牌 -> access token过期后用旧refresh token获取新令牌 -> 旧refresh token因Redis中已更新为新令牌而失效 -> 下次必须用新的refresh token才能刷新。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 09:54:07