JWT
JWT 定义
JWT(JSON Web Token) 是一个基于 JSON 的开放标准(RFC 7519),用于创建access token,是目前最流行的跨域认证解决方案。简单来说,一个 JWT 就是一个字符串,形式如下:
header.payload.signature;
session 认证的问题

如上图,传统使用session进行身份认证的流程如下:
- 客户端向服务器发送用户名和密码。
- 服务器验证通过后,在当前对话(
session)里面保存相关数据,比如用户名、过期时间等。 - 服务器通过传送
Set-Cookie将session_id写入用户的Cookie。 - 用户随后的每一次请求,都会通过
Cookie将session_id传回服务器。 - 服务器收到
session_id,通过与数据库保存的数据进行匹配,由此认证用户的身份。
通过上述流程可以看出,服务器需要对用户的session信息进行存储,并通过匹配session_id来辨别用户身份,因此,服务器需要维护大量的session信息。而对于服务器集群来说,由于不同的系统之间session是不共享的,因此需要将所有系统的session做抽象处理,大大的加大了开发难度。
除此之外,我们知道cookie是不能够跨域的,也就是上面的模型对于不同域名的应用并不适用。
为什么使用 JWT

JWT-Token认证流程
如图所示,存在 3 个角色:
- Authentication server (认证服务器)
- User(用户)
- Application server (应用服务器)
步骤:
- 用户使用账号密码在登录页登录,并将账号和密码传给认证服务器。
- 认证服务器生成
Token并将其传给用户。 - 用户通过携带
Token的API请求访问应用服务器。 - 应用服务器通过
JWT来判断用户身份,通过验证后将数据返回给用户。
JWT有如下几个优点:
JWT一套无状态的验证机制,用户访问时通过Token进行身份验证,服务器不用为了存储用户状态来维护session,服务器可以做到更多的解耦和扩展。JWT中的Token可以通过URL和请求头字段Authorization进行传送,可以避免跨域问题。JWT可以通过payload保存用户的数据,减少数据库访问。
JWT 创建 Token
创建 header
header 部分是一个 JSON 对象,用来描述 JWT 的元数据,如下所示:
{
"typ": "JWT", // 表明是 JWT
"alg": "HS256" // 代表生成 signature 所用的哈希算法,这里是 HMAC-SHA256
}
生成Token时需要,将上面的 JSON 对象使用 Base64URL 算法转成字符串。
创建 payload
JWT 的 payload 部分用来存放实际需要传递的数据。
比如我们这里存储了用户 ID:
{
"userId": "b08f86af-35da-48f2-8fab-cef3904660bd"
}
你可以在 payload 里存储大量信息,但大量信息会降低性能,增加延迟。
与header相同,payload也需要使用Base64URL 算法转成字符串。
计算生成 signature
header 和 payload 使用的是 Base64URL 编码,所以将两者分别传入base64UrlEncode中,并结合服务器生成的secret使用下面的公式计算出signature。其中HMAC SHA256是 Header 里面默认指定的签名算法。
// signature algorithm
HMACSHA256(base64UrlEncode(header) + '.' + base64UrlEncode(payload), secret);
组装 header,payload 和 signature。把 header,payload 和 signature 用 . 相连即最终的 JWT token。
例如:当header的值为"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9",payload的值为"eyJ1c2VySWQiOiJiMDhmODZhZi0zNWRhLTQ4ZjItOGZhYi1jZWYzOTA0NjYwYmQifQ",signature的值为"-xN_h82PHVTCMA9vdoHrcZxH-x5mb11y1537t3rGzcM"时,生成的Token如下所示:
// header.payload.signature;
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJiMDhmODZhZi0zNWRhLTQ4ZjItOGZhYi1jZWYzOTA0NjYwYmQifQ.-xN_h82PHVTCMA9vdoHrcZxH-x5mb11y1537t3rGzcM
JWT 使用方式
客户端收到认证服务器返回的 Token,可以将其存在 Cookie或localStorage/sessionStorage中。
当客户端访问应用服务器时,每次请求都需要带上Token。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求头的Authorization字段里面,如下所示:
Authorization: Bearer <token>
在跨域时,除了上述用法外,还可以把Token就放在 POST 请求的数据体里。
验证 JWT token
因为应用服务器知道验证服务器哈希计算 signature 的 secret,所以应用服务器可以用 secret 去重新计算 signature (用户发送过来的 token 里有 header 和 payload),并与用户发送过来的 token 中 signature 比较,最终验证是否合法。
安全性
我们在前面提到的header 和 payload并没有进行加密,只是经过了base64URL的编码,所以“黑客”不需要secret就能获取到header和payload中的敏感信息。此外,“黑客”还可以通过修改header中alg的参数值为none,对于那些支持alg字段为none的服务器,后端将不会进行签名验证。
除了上述两种案例外,还有很多其他的风险案例,如果感兴趣可以参考这篇文章:JWT 介绍及其安全性分析。因此,为了保证数据的安全性,JWT一般需要结合https一起使用。