JWT 鉴权实现指南:签发、校验与刷新令牌

JSON Web Token(JWT)是一个开放标准(RFC 7519),用于在各方之间以紧凑、自包含的方式安全传输信息。因为令牌可以数字签名,接收方可以验证其完整性和来源。jwt.io 是行业公认的参考实现与资料站,本文依据其官方文档和 Auth0 的工程实践,梳理一套可落地的 JWT 鉴权方案。JWT 常与 OAuth2 与 JWT 接口安全指南 搭配使用,本文聚焦令牌本身。

一、JWT 的结构:三段式

一个 JWT 由点号分隔的三部分组成:xxxxx.yyyyy.zzzzz

  1. Header(头部):声明令牌类型和签名算法,例如 {"alg":"HS256","typ":"JWT"}
  2. Payload(载荷):包含声明(claims)。注册声明如 iss(签发者)、exp(过期时间)、sub(主题)、aud(受众);还有自定义声明。注意:签名令牌的内容任何人都能读取,绝不能放密码、密钥等敏感信息。
  3. Signature(签名):对 base64UrlEncode(header) + "." + base64UrlEncode(payload) 用密钥签名得到,用于验证内容未被篡改、且确实由持有私钥/密钥的一方签发。

在 jwt.io 的 Debugger 里粘贴一个真实令牌会更直观(以下为演示用的 HS256 令牌):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

注意中间那段 payload 只是 Base64 编码,任何人都能解码看到 name 字段——这正好印证了"不要往载荷里放敏感信息"那条原则。

二、签发与校验

签发流程:用户登录成功后,服务端生成 JWT 返回给客户端。校验流程:客户端在后续请求的 Authorization 头携带令牌:

Authorization: Bearer <token>

服务端需要区分"验证"与"校验"两层:验证(validation)检查令牌结构、格式和声明是否合法(如是否过期、是否在生效时间之前);校验(verification)用算法和密钥重算签名,确认令牌真实且未被篡改,并核对 issaud 是否匹配预期。成熟语言的官方库都封装了这些逻辑,不要自己拼签名。

以 Node.js 的 jsonwebtoken 为例,签发与校验各只需几行:

const jwt = require('jsonwebtoken');

// 登录成功后签发,过期时间要短
const token = jwt.sign({ userId: user.id, role: 'admin' }, SECRET, {
  expiresIn: '15m',
  issuer: 'my-api',
});

// 请求到达时校验
try {
  const payload = jwt.verify(token, SECRET, {
    issuer: 'my-api',
    algorithms: ['HS256'],
  });
  req.user = payload;
} catch (err) {
  res.status(401).json({ error: 'invalid token' });
}

校验时显式传入 issueralgorithms 白名单,可以防住"算法混淆"这类攻击(攻击者把 alg 改成 none 或降级到弱算法时会被直接拒绝)。

三、刷新令牌:延长会话

访问令牌(access token)过期时间通常很短(15 分钟到 1 小时),过期后用户不能直接重新登录,而是用**刷新令牌(refresh token)**换取新的访问令牌。Auth0 的实践建议:

  • 刷新令牌生命周期更长(数天到数月),只发给受信任的客户端。
  • 刷新令牌要可撤销:服务端保存其标识,检测到泄露或退出登录时立即作废。
  • 刷新接口要校验刷新令牌本身的签名与有效期,并返回新的访问令牌和(可选)新的刷新令牌。

两类令牌的分工可以这样理解:

令牌 有效期 存放位置 用途
access token 15 分钟-1 小时 内存或短期变量 每次 API 请求携带
refresh token 数天-数月 仅受信任客户端、HttpOnly Cookie 换取新的 access token

把刷新令牌放进 HttpOnly Cookie,JavaScript 就读取不到,XSS 窃取面大幅收窄;访问令牌放内存而非 localStorage,页面刷新后重新走一次刷新流程即可。

刷新令牌本质是"长期凭证",必须像密码一样保护,不要存储到浏览器 localStorage 等易受 XSS 窃取的位置。

四、安全最佳实践

  • 短过期 + 刷新:访问令牌尽量短,降低泄露窗口;用刷新令牌续期。
  • 不要存敏感数据:payload 会以 Base64 可读,勿放密钥、内部 ID 等。
  • 头大小限制:令牌放在 HTTP Header 传输,部分服务器限制 8KB,别塞太多权限声明。
  • 算法选择:内部系统可用 HS256(共享密钥);跨服务、需要验证签发方身份时用 RS256/ES256(公私钥对)。
  • 使用成熟库:jwt.io 的 Libraries 页面列出了各语言的推荐实现,避免手写加密逻辑踩坑。

还有一个容易被忽略的细节是密钥轮换:HS256 用共享密钥,一旦泄露,过去签发的所有令牌都失去信任,所以要定期轮换,并让校验端在新旧密钥并存期内同时接受两者,避免切换瞬间把在线用户全部踢下线。审计方面,把签发、校验失败与刷新次数都记入日志,某项指标异常暴涨,往往意味着有人在扫描或爆破。

如果只是做前后端分离的网站登录,先读完 API 安全 OAuth2 与 JWT 指南 再动手;落地时可以参考 Node.js Express 后端开发指南Python FastAPI 后端开发指南 中的中间件接入方式。

16IDC 观察

对独立开发者和小团队,JWT 是性价比最高的 API 鉴权方案:无状态、跨语言、容易横向扩展。但"无状态"也意味着令牌一旦签发就无法主动作废,所以刷新令牌的可撤销机制和合理的过期时间格外重要。上线前把鉴权中间件、限流和 API 错误处理与重试策略 一起规划好,能少踩很多坑。更多后端工程实践见 后端对接 分类。

参考:jwt.io 入门 https://jwt.io/introduction · RFC 7519(JSON Web Token)https://datatracker.ietf.org/doc/html/rfc7519 · Auth0 刷新令牌实践 https://auth0.com/docs/secure/tokens/refresh-tokens
原文来源:https://jwt.io/introduction