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

Flutter+NodeJS抽奖功能:如何验证用户的最新JWT令牌?

解决方案:无需数据库验证最新中奖令牌

针对你的需求,以下几个轻量方案无需依赖独立数据库,就能在Node.js后端便捷验证中奖令牌是否为最新版本:

方案1:JWT嵌入抽奖批次号 + 内存存储当前有效批次号

每次抽奖生成唯一的批次号(比如时间戳、递增数字),将其存入Node.js内存,同时把批次号写入中奖用户的JWT中。验证时只需对比JWT中的批次号与内存中的当前有效批次号即可。

代码示例

const jwt = require('jsonwebtoken');

// 内存存储当前有效抽奖批次号,启动时初始化
let currentValidBatchId = null;

// 抽奖逻辑:生成批次号并发放JWT
function drawWinner(winnerId) {
  // 用时间戳生成唯一批次号
  const newBatchId = Date.now().toString();
  currentValidBatchId = newBatchId;

  // 生成包含批次号的JWT,设置权益有效期(示例7天)
  const token = jwt.sign(
    { userId: winnerId, batchId: newBatchId },
    'your-strong-secret-key',
    { expiresIn: '7d' }
  );
  return token;
}

// 令牌验证中间件
function validateWinnerToken(req, res, next) {
  const token = req.headers['authorization']?.split(' ')[1];
  if (!token) return res.status(401).send('未提供令牌');

  try {
    const decoded = jwt.verify(token, 'your-strong-secret-key');
    // 校验批次号是否为当前有效版本
    if (decoded.batchId !== currentValidBatchId) {
      return res.status(403).send('令牌无效或已过期');
    }
    req.winnerInfo = decoded;
    next();
  } catch (err) {
    return res.status(403).send('令牌无效');
  }
}

优势

  • 完全无需数据库,验证逻辑极简,性能损耗可忽略
  • 批次号更新仅需覆盖内存值,操作成本极低

方案2:JWT的jti声明 + 内存黑名单

利用JWT的jti(JWT唯一ID)字段,每次抽奖生成新的jti,并将上一批次的jti加入内存黑名单。验证时检查令牌的jti是否在黑名单中,或是否与最新有效jti一致。

代码示例

const jwt = require('jsonwebtoken');
const crypto = require('crypto');
const LRU = require('lru-cache');

// 用LRU缓存维护黑名单,限制存储数量并自动过期(与JWT有效期一致)
const invalidJtiCache = new LRU({ 
  max: 100, 
  ttl: 7 * 24 * 60 * 60 * 1000 // 7天过期
});

let latestValidJti = null;

function drawWinner(winnerId) {
  const newJti = crypto.randomUUID();
  // 将上一批次的有效jti加入黑名单
  if (latestValidJti) {
    invalidJtiCache.set(latestValidJti, true);
  }
  latestValidJti = newJti;

  const token = jwt.sign(
    { userId: winnerId, jti: newJti },
    'your-strong-secret-key',
    { expiresIn: '7d' }
  );
  return token;
}

function validateWinnerToken(req, res, next) {
  const token = req.headers['authorization']?.split(' ')[1];
  if (!token) return res.status(401).send('未提供令牌');

  try {
    const decoded = jwt.verify(token, 'your-strong-secret-key');
    // 校验jti是否在黑名单,或是否为最新有效jti
    if (invalidJtiCache.has(decoded.jti) || decoded.jti !== latestValidJti) {
      return res.status(403).send('令牌无效或已过期');
    }
    req.winnerInfo = decoded;
    next();
  } catch (err) {
    return res.status(403).send('令牌无效');
  }
}

优势

  • 黑名单仅存储旧批次的jti,内存占用极小
  • 结合LRU缓存自动清理过期条目,无需手动维护

方案3:内存存储最新令牌哈希值

每次生成新的中奖令牌后,将令牌的哈希值(如SHA256)存入内存。验证时计算收到令牌的哈希值,与内存中的哈希对比一致则有效。

代码示例

const jwt = require('jsonwebtoken');
const crypto = require('crypto');

let latestTokenHash = null;

function drawWinner(winnerId) {
  const token = jwt.sign(
    { userId: winnerId },
    'your-strong-secret-key',
    { expiresIn: '7d' }
  );
  // 生成令牌哈希存入内存
  latestTokenHash = crypto.createHash('sha256').update(token).digest('hex');
  return token;
}

function validateWinnerToken(req, res, next) {
  const token = req.headers['authorization']?.split(' ')[1];
  if (!token) return res.status(401).send('未提供令牌');

  try {
    // 先验证JWT签名防止伪造
    const decoded = jwt.verify(token, 'your-strong-secret-key');
    // 对比哈希值判断是否为最新令牌
    const tokenHash = crypto.createHash('sha256').update(token).digest('hex');
    if (tokenHash !== latestTokenHash) {
      return res.status(403).send('令牌无效或已过期');
    }
    req.winnerInfo = decoded;
    next();
  } catch (err) {
    return res.status(403).send('令牌无效');
  }
}

优势

  • 逻辑最简单,无需解析JWT内容即可快速完成哈希对比
  • 内存仅存储一个哈希值,资源占用极低

注意事项

  1. 内存持久化问题:如果Node.js进程重启,内存中的批次号/jti/哈希会丢失。若需持久化,可选用轻量内存数据库(如Redis);若重启后可重新触发抽奖或标记期间权益无效,纯内存方案完全可行。
  2. JWT安全:务必使用强签名密钥,且通过HTTPS传输令牌,防止伪造或拦截。
  3. 过期时间:建议将JWT过期时间设置为与权益有效期一致,即使令牌未被替换,过期后也会自动失效,降低安全风险。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 03:16:18