JWT(JSON Web Tokens) 是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准。它的设计是紧凑且安全的,特别适用于分布式站点的单点登录(SSO)。
JWT的声明一般用来在身份提供者和服务者间传递被验证的用户身份信息,以便于从服务器获取资源。
1. why need it ?
一般的登录流程大致为:
(1) 用户输入用户名and密码,点击登录。
(2) 客户端把输入的数据传给服务器。
(3) 服务器收到请求后,验证用户名及密码。验证成功的话,服务器会将登录凭证做数字签名并进行加密得到一个 token 返回给客户端,并在自己对应的 session 里记录用户的登录状态为已登录。
(4) 客户端收到 token 后,会把 token 存起来「 cookie or localStorage 」
(5) 后面客户端每次向服务器请求资源的时候都会带上这个 token 进行验证。「 请求头中携带 」
这种模式的问题在于:扩展性不好,如果是服务器集群, 那就要求 session 共享才能实现服务器都可以读取到用户的登录状态。
2. how to work ?
JWT 的原理是,服务器验证之后,生成一个 JSON 对象,返回给客户端,之后用户每次与服务器进行交互时,都需要带上这个 JSON 对象。服务器会根据这个对象进行身份认证。当然为了防止用户篡改数据,服务器会在生成这个对象的时候加上签名。
这样,服务器就不用保存任何的 session 数据了,也就是服务器不需要记录下用户的登录状态了。
3. data structure
JWT 是一个很长的字符串,由三个部分构成,中间用 . 隔开。三个部分分别为:Header、Payload、Signature。
3.1 Header
Header 部分本质上是一个 json 对象,用来描述 JWT 的元数据,有两个基本属性:
{
"alg":"HS256",
"typ":"JWT"
}
- alg 属性表示签名所使用的算法,默认采用 HMAC SHA256(简写为 HS256)
- typ 属性表示改 token 的类型,JWT 令牌当然就是 JWT
当然,要用 Base64URL 算法把上面的 JSON 对象转为字符串。
3.2 Payloader
Payloader 部分本质上也是一个 JSON 对象。用来存放实际上要传输的数据,有七个基本属性:
- iss -> 该 JWT 的签发者
- nbf -> 该 JWT 的生效时间
- aud -> 该 JWT 的用户
- exp -> 该 JWT 的过期时间戳
- iat -> 该 JWT 的签发时间
- jti -> 该 JWT 的编号(id)
- sub -> 该 JWT 主题
「也支持自定义属性」
{
"iss":"yt JWT",
"nbf":"1720151588198",
"name":"yt",
"admin":true
}
注意:
JWT 默认是不加密的,任何人都可以读到,所以不要把重要私密信息放入该部分。
同理,这个 JSON 对象也要用 Base64URL 方法转为字符串。
3.3 Signature
Signature 是根据前两部分生成的签名,防止数据被篡改。
生成过程:
(1) 指定一个密钥 secret「只有服务器知道」。 (2) 使用 Header 中 "alg" 字段指定的签名算法来生成签名。HS256的大致逻辑如下。
HMACSHA256(
base64UrlEncode(header) + '.' + base64UrlEncode(payload),
secret
)
签名生成之后,就把 Header 、Payload 、Signature 三部分用'.' 连接生成一个字符串并返回给用户。
【插播一下: Base64URL】
用 Base64URL 加工生成的字符串中可能会包含三个字符 '+' 、'/'、'=' ,而这三个字符在 URL 中是有特殊含义的(生成的字符串有时候会被作为 token 参数携带在 URL 中),所以这三个字符就需要做特殊处理 :
- '=' 被省略
- '+' 被替换为 '-'
- '/' 被替换为 '_'
4. How to use ?
客户端收到服务器返回的 JWT,可以存储在 cookie 里,也可以存储在 locakStorage 里。此后每次客户端与服务器之间的交互通信都要带上这个 JWT。
- 放在 Cookie 里的话,每次请求都会被自动带上,缺点是不能跨域。
- 比较好的做法是,通过请求头带过去,即放在Authorization字段里。