简介
JSON Web Token(JWT)是目前常用的跨域认证方案。
基于 session 的认证在服务器集群或多系统场景中难以实现一次登录全局可用。解决方案主要有两种:
- 将 session 数据持久化,所有系统统一访问持久层;
- 服务器不保存 session,把认证数据交给客户端保存,每次请求携带令牌。
JWT 就属于第二种方案。
用户认证(Authentication)确认用户身份,例如登录;用户授权(Authorization)校验是否具备操作权限,例如是否能修改
/etc/hosts
。
原理
首次认证通过后,服务器生成 JSON 对象返回客户端,如:
{
"name": "张三",
"role": "管理员",
"exp": "1530374400"
}
之后客户端在每次请求中携带该对象,服务器根据令牌确定身份。为防止篡改,服务器会使用密钥对令牌进行签名,密钥只保存在服务器端。
数据结构
JWT 由三部分组成:Header、Payload、Signature,使用 .
拼接。
Header
Header 描述元数据:
{
"alg": "HS256",
"typ": "JWT"
}
alg
:签名算法,默认 HMAC SHA256;typ
:令牌类型,写JWT
。
该 JSON 需通过 Base64URL 编码。
Payload
Payload 存放业务数据,JWT 规范内置字段包括:iss
、exp
、sub
、aud
、nbf
、iat
、jti
等。也可自定义字段,但 不要存放敏感信息,因为默认不加密。
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 体中。
特点与注意事项
- JWT 默认不加密,必要时可以对最终令牌再加密一次。
- 未加密的 JWT 不应包含敏感信息。
- JWT 可减少数据库查询,但令牌一旦签发,除非有额外机制,否则到期前始终有效。
- 令牌泄露会导致权限被夺取,建议缩短有效期并对高敏感操作追加验证。
- 使用 HTTPS 传输 JWT,避免明文截获。