5月27日 23:09

如何优化 JWT 的性能?

核心优化方向有三个:减小 Token 体积加速签名验证避免重复解析

减小 Token 体积

JWT 每次请求都携带,体积直接影响带宽和解析耗时。

javascript
// 只存必要字段,用短键名 const token = jwt.sign({ uid: "123", rol: "admin" }, SECRET); // 而非 { userId, userName, userRole, userPermissions }
  • Payload 只放用户 ID 和角色,其他信息从缓存取
  • 避免存数组、嵌套对象,每个字段都计入 Base64 编码后的体积
  • 压缩(如 pako deflate)仅在 Payload > 1KB 时有意义,小 Payload 压缩后反而更大

加速签名验证

算法选择对性能影响最大:

算法签名速度验证速度Token 大小适用场景
HS256最快最快~32B单体应用,密钥可安全共享
RS256~256B微服务,公钥可公开分发
ES256~64B移动端/IoT,兼顾速度与体积
  • HS256 比 RS256 快 5-10 倍,但密钥泄露风险更高
  • ES256 是分布式场景的平衡选择:签名比 RS256 小 4 倍,验证更快
  • 如果只用 HS256,可手写 HMAC 验证替代 jsonwebtoken 库,减少框架开销

避免重复解析

每次请求都 jwt.verify() 是浪费,可用缓存跳过:

javascript
// 验证结果缓存(注意安全风险) const NodeCache = require("node-cache"); const cache = new NodeCache({ stdTTL: 300 }); // 缓存 5 分钟 function verifyCached(token) { const key = `tk:${crypto.createHash("sha256").update(token).digest("hex")}`; const hit = cache.get(key); if (hit) return hit; const decoded = jwt.verify(token, SECRET); cache.set(key, decoded); return decoded; }

缓存的安全代价:Token 被撤销(如用户登出)后,缓存期内仍可访问。解法:

  • 缓存 TTL 设短(< Token 有效期的 1/10)
  • 登出时主动清除对应用户的缓存 key
  • 高安全场景不缓存,用算法优化替代

多实例部署时用 Redis 替代本地缓存,公钥(JWKS)也做缓存避免每次远程拉取。

异步验证

javascript
// 同步验证阻塞事件循环 const decoded = jwt.verify(token, SECRET); // 阻塞 // 异步验证释放事件循环 const decoded = await new Promise((resolve, reject) => { jwt.verify(token, SECRET, (err, d) => err ? reject(err) : resolve(d)); });

高并发下同步 crypto 操作会阻塞 Node.js 事件循环,异步 + worker_threads 才是正解。

追问

Q: 缓存验证结果后,Token 撤销怎么办? 上面已提到,核心思路是短 TTL + 主动失效。也可以用 Token 版本号:用户登出时递增版本,缓存中版本不匹配则重新验证。

Q: JWT 无状态的意义在哪,加了缓存不就有状态了? 缓存是性能优化手段,不是架构依赖。缓存挂掉系统仍可用(降级为全量验证),这和 Session 必须依赖 Redis 有本质区别。

Q: 什么时候该用 JWT,什么时候该用 Session? 多服务/跨域/移动端选 JWT;单体内聚应用选 Session 更简单。JWT 的性能代价换来的是水平扩展能力,不是所有场景都需要。

标签:JWT