简介

JSON Web Token(JWT)是目前常用的跨域认证方案。

基于 session 的认证在服务器集群或多系统场景中难以实现一次登录全局可用。解决方案主要有两种:

  1. 将 session 数据持久化,所有系统统一访问持久层;
  2. 服务器不保存 session,把认证数据交给客户端保存,每次请求携带令牌。

JWT 就属于第二种方案。

用户认证(Authentication)确认用户身份,例如登录;用户授权(Authorization)校验是否具备操作权限,例如是否能修改 /etc/hosts

原理

首次认证通过后,服务器生成 JSON 对象返回客户端,如:

{
    "name": "张三",
    "role": "管理员",
    "exp": "1530374400"
}

之后客户端在每次请求中携带该对象,服务器根据令牌确定身份。为防止篡改,服务器会使用密钥对令牌进行签名,密钥只保存在服务器端。

数据结构

JWT 由三部分组成:Header、Payload、Signature,使用 . 拼接。

Header 描述元数据:

{
    "alg": "HS256",
    "typ": "JWT"
}
  • alg:签名算法,默认 HMAC SHA256;
  • typ:令牌类型,写 JWT

该 JSON 需通过 Base64URL 编码。

Payload

Payload 存放业务数据,JWT 规范内置字段包括:issexpsubaudnbfiatjti 等。也可自定义字段,但 不要存放敏感信息,因为默认不加密。

Payload 同样使用 Base64URL 编码。

Signature

Signature 用于防止数据篡改,生成步骤:

import hmac
from base64 import urlsafe_b64encode

hmac.new(
    secret,
    urlsafe_b64encode(header.encode()) + b"." + urlsafe_b64encode(payload.encode()),
    "sha256",
).hexdigest()

将 Header、Payload、Signature 用 . 拼接得到最终令牌。

Base64URL

JWT 作为一个令牌(token),有些场合可能会放到 URL 中,例如 api.example.com/?token=xxx。

Base64 有三个字符 +/=,在 URL 里面有特殊含义,所以要被替换掉。Base64URL 将 +/= 分别替换为 -_、省略,以便在 URL 中使用。

使用场景

简单认证

在退订或问卷链接中携带 JWT,可免登录完成低风险操作,同时避免伪造链接。

单点登录

同一顶级域名下可将 JWT 写入 cookie(设置 domain 为顶级域名)。跨域场景可将 JWT 放入 Authorization: Bearer <token> 请求头或 POST 体中。

特点与注意事项

  1. JWT 默认不加密,必要时可以对最终令牌再加密一次。
  2. 未加密的 JWT 不应包含敏感信息。
  3. JWT 可减少数据库查询,但令牌一旦签发,除非有额外机制,否则到期前始终有效。
  4. 令牌泄露会导致权限被夺取,建议缩短有效期并对高敏感操作追加验证。
  5. 使用 HTTPS 传输 JWT,避免明文截获。

参考链接