JWT 鉴权实现指南:签发、校验与刷新令牌
JSON Web Token(JWT)是一个开放标准(RFC 7519),用于在各方之间以紧凑、自包含的方式安全传输信息。因为令牌可以数字签名,接收方可以验证其完整性和来源。jwt.io 是行业公认的参考实现与资料站,本文依据其官方文档和 Auth0 的工程实践,梳理一套可落地的 JWT 鉴权方案。JWT 常与 OAuth2 与 JWT 接口安全指南 搭配使用,本文聚焦令牌本身。
一、JWT 的结构:三段式
一个 JWT 由点号分隔的三部分组成:xxxxx.yyyyy.zzzzz。
- Header(头部):声明令牌类型和签名算法,例如
{"alg":"HS256","typ":"JWT"}。 - Payload(载荷):包含声明(claims)。注册声明如
iss(签发者)、exp(过期时间)、sub(主题)、aud(受众);还有自定义声明。注意:签名令牌的内容任何人都能读取,绝不能放密码、密钥等敏感信息。 - Signature(签名):对
base64UrlEncode(header) + "." + base64UrlEncode(payload)用密钥签名得到,用于验证内容未被篡改、且确实由持有私钥/密钥的一方签发。
在 jwt.io 的 Debugger 里粘贴一个真实令牌会更直观(以下为演示用的 HS256 令牌):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
注意中间那段 payload 只是 Base64 编码,任何人都能解码看到 name 字段——这正好印证了"不要往载荷里放敏感信息"那条原则。
二、签发与校验
签发流程:用户登录成功后,服务端生成 JWT 返回给客户端。校验流程:客户端在后续请求的 Authorization 头携带令牌:
Authorization: Bearer <token>
服务端需要区分"验证"与"校验"两层:验证(validation)检查令牌结构、格式和声明是否合法(如是否过期、是否在生效时间之前);校验(verification)用算法和密钥重算签名,确认令牌真实且未被篡改,并核对 iss 和 aud 是否匹配预期。成熟语言的官方库都封装了这些逻辑,不要自己拼签名。
以 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' });
}
校验时显式传入 issuer 与 algorithms 白名单,可以防住"算法混淆"这类攻击(攻击者把 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