JSON Web Token

在用Nginx做负载均衡时不得不考虑到一个问题——sessionid共享。如果通过IP绑定来防止sessionid共享则容易破坏服务器集群负载均衡的稳定性。但通过redis等技术(数据持久化方案)又无法避免整个服务器集群之间共享sessionid。为了解决这个问题,JSON Web Token诞生了。

定义

JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。它不再依赖服务端保存认证信息(session),而是将所有用户的认证信息交由客户端(浏览器等)保管,每次用户请求必须携带这些信息,服务器通过分析这些信息来验证用户。

原理

JWT的原理是,服务器认证以后,生成一个由 JSON 字符串(Base64URL转码)或普通字符串拼接而成的大字符串,让用户保存。JSON的内容可能如下:

1
2
3
4
5
{
"name": 123,
"token": "fadfdgdsag",
"maxAge": 30
}

数据结构

JWT的认证数据是由三个部分组成的:

  1. Header(头部)
  2. Payload(负载)
  3. Signature(签名)

这三个部分由.标识符分隔组成一个字符串:

1
2
"abadfdsafdsaf.afdsafdsafsadfdsagfg.afdsafdsafasf"
// Header.Playload.Signature

Header部分是一个JSON对象,描述 JWT 的元数据,通常是下面的样子:

1
2
3
4
{
"alg": "HS256",
"typ": "JWT"
}

上面代码中,alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT

最后,将上面的 JSON 对象使用 Base64URL 算法转成字符串。

Payload

Payload部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用:

字段 含义
iss (issuer) 签发人
exp (expiration time) 过期时间
sub (subject) 主题
aud (audience) 受众
nbf (Not Before) 生效时间
iat (Issued At) 签发时间
jti (JWT ID) 编号

当然,你也可以在其中加入一些自定义的私有字段。最后,将上面的 JSON 对象使用 Base64URL 算法转成字符串。

Signature

Signature不同于上面两个字段,它是用以防止数据篡改的标识,通过复杂的算法(在Header中指定的alg)把编码后的前两个字段加上服务器端生成的密钥合成一个难以伪造的标签:

1
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret);

Base64URL类似于Base64编码,但它新添了转义URL中特定标识的功能:如=被忽略,+转义成-/转义成_

JWT存储

JWT最好避免使用cookie存储,这样不利于跨域,同时也无法避免cookie禁用的情况。可以通过localStorage存储。

JWT发送

JWT默认是不加密的(上述加密过程安全性仍然不高,虽然可以二次加密),所以应通过https服务来进行发送。此外,应该将JWT存储在http中的Authorization,或者放在post的请求体中。

1
Authorization: Bearer <token>

缺陷

不同于session,面对JWT时,服务器是无状态的。因此,只要在JWT有效期内,服务器是没有办法让JWT失效的。因此,不应将其有效时间设置过长。

参考

《JSON Web Token 入门教程》——阮一峰