停止使用 JWT 库:如何使用 node:crypto 构建自己的轻量令牌

发布: (2026年2月1日 GMT+8 17:53)
4 min read
原文: Dev.to

Source: Dev.to

封面图片:停止使用 JWT 库:如何使用 node:crypto 构建自己的轻量级令牌

有没有在 node_modules 里看到,只是为了给一个小的 JSON 对象签名,就要引入一个庞大的库?

如果你在构建 Node.js 后端,你已经拥有了一个内置的强大安全工具:node:crypto 模块。

今天,我们将构建一个自定义、可靠的身份验证令牌系统,它比标准的 JWT 库更快、更轻量,也更安全。

为什么要跳过库?

  • 零依赖 – 没有 jsonwebtoken,就少了一个需要审计漏洞的包。
  • 性能node:crypto 是 Node 中的原生 C++ 绑定,速度极快。
  • 防止“算法切换”攻击 – 大多数 JWT 漏洞都是因为库允许客户端选择算法(例如 alg: none)。在我们的代码中我们硬编码了安全性。

逻辑

一个安全的令牌由两部分组成:

  1. 载荷(Payload) – 一个 Base64URL 编码的字符串,包含用户数据(ID、用户名、头像)。
  2. 签名(Signature) – 一个 HMAC(基于哈希的消息认证码),用来证明载荷未被篡改。

1. 签名函数

import { createHmac } from "node:crypto";

const TOKEN_SECRET = process.env.TOKEN_SECRET;

export const signToken = (payload: object) => {
  // Add 15‑minute expiration
  const claims = {
    ...payload,
    exp: Date.now() + 15 * 60 * 1000,
  };

  // 1. Encode data to a URL‑safe string
  const encodedPayload = Buffer.from(JSON.stringify(claims)).toString("base64url");

  // 2. Create the signature (the "security seal")
  const signature = createHmac("sha256", TOKEN_SECRET!)
    .update(encodedPayload)
    .digest("base64url");

  // 3. Combine them with a dot
  return `${encodedPayload}.${signature}`;
};

2. 验证函数

import { createHmac, timingSafeEqual } from "node:crypto";

export const verifyToken = (token: string) => {
  const [encodedPayload, signature] = token.split(".");
  if (!encodedPayload || !signature) throw new Error("Malformed token");

  // Re‑calculate what the signature should be
  const expectedSignature = createHmac("sha256", process.env.TOKEN_SECRET!)
    .update(encodedPayload)
    .digest("base64url");

  // Use timingSafeEqual for high‑level security
  const isValid = timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );

  if (!isValid) throw new Error("Invalid signature!");

  const claims = JSON.parse(Buffer.from(encodedPayload, "base64url").toString());

  // Check if expired
  if (Date.now() > claims.exp) throw new Error("Token expired");

  return claims;
};

专业提示:注意你的 Header 大小!

因为我们在令牌里放入了“重量级”数据,如头像和用户名,字符串可能会变得很长。

  • 缩短键名:使用 u 表示用户名,使用 img 表示头像。
  • 保持精简:只存储 UI 立即需要的数据。

结论

使用 node:crypto,你可以完全掌控自己的身份验证。你不仅仅是在跟随教程,而是理解了保障网络安全的数学和逻辑。

你还在使用 JWT 库吗,还是已经转向原生 crypto 了?在评论区告诉我吧!

Back to Blog

相关文章

阅读更多 »

开启 RUST

我的 Java 到 Rust 的转变之路:更换 technology stack 你好,我叫 Garik,今天我想与大家分享我决定更换 technology stack 的故事。