JWT(JSON Web Token) 原理介绍
自从 RESTful 架构兴起后,越来越多的人提倡使用 JWT 来取代传统 Session 的场景,究竟什么是 JWT 呢,来了解下吧~
JWT 介绍
-
JWT 是 JSON Web Token 的缩写,是一种开放标准(RFC 7519),即基于 JSON 对象的编码,并通过这个编码传递信息。 -
JWT 会通过 HMAC、RSA、ECDSA 等算法进行加密。 -
通常利用 JWT 来对用户进行验证,也就是说用户会先请求身份凭证服务器拿到该JWT,然后,只要用户携带这个 JWT 向业务服务器请求资源,如果这个 JWT 是有效的,那么就能获取资源。
JWT 的组成
JWT 的结构可以看作是三个 JSON 对象,并且用点(.)来分隔,这三个部分会各自进行编码,组成一个 JWT 字符串。也就是变成:xxxxx.yyyyy.zzzzz
Header(头部)
由两部分组成:
-
alg(算法) 也就是 token 被加密的算法,如 HMAC、SHA256、RSA。 -
typ(类型) 也就是 token 的类型,基本上就是 JWT。
示例:
{
"alg": "HS256",
"typ": "JWT"
}
然后进行 Base64 进行编码。Base64 是通过 64 个字符来表示二进制数据的一种方法,编码的方式是固定的而且可以逆向解码的,并不是那种安全的加密算法。
Payload(负载)
这里放的是声明(Claim)内容,也就是用来传递消息的地方,在定义上有三种声明:
1. Registered claims
可以看作是标准公认的一些消息,建议放,但并不强迫,例如:
-
iss(Issuer):JWT 签发者 -
exp(Expiration Time):JWT 的过期时间,过期时间必须大于签发 JWT 时间 -
sub(Subject):JWT 所面向的用户 -
aud(Audience):接收 JWT 的一方 -
nbf(Not Before):也就是定义拟发放 JWT 之后,的某段时间点前该 JWT 仍旧是不可用的 -
iat(Issued At):JWT 签发时间 -
jti(JWT Id):JWT 的身份标识,每个 JWT 的 Id 都应该是不重复的,避免重复发放
2. Public claims
这个,可以看作是传递的字段必须与上面的 Registered claims 字段不能冲突,然后可以向官方申请定义公开声明,会进行审核等步骤,实际上在开发上是不太会用这部分的。
3. Private claims
这是发放 JWT 服务器可以自定义的部分,例如实际上会放 User Account、User Name、User Role 等不敏感的数据。
所谓不敏感的数据就是不会放用户的密码等敏感数据,因为该 Payload 传递的消息最后也是通过 Base64 进行编码,所以是可以被破解的,因此放用户密码会有安全性的问题。
示例:
{
"sub": "1234567890",
"account": "kenny@example.com",
"role": "admin"
}
个人感觉通常都会放 iat、exp 等标准字段,因为通常需要检查 JWT 发送时间及是否过期,以及还有用户账号,为了方便查询用户的一些数据,通常以前的做法是 Session 里面存放用户账号,现在改用 JWT 的 payload 上存放,以及角色身份的定义,可以用来看该用户是否有权限获取后端 API 的数据。
Signature(签名)
由三大部分組成:
-
base64UrlEncode(header) -
base64UrlEncode(payload) -
secret
也就是:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
header 和 payload 中间用点(.)来连接,secret 是存放在服务器端的加密字符串,最后将这三个部分串接再一起的字符串进行加密算法进行加密。
secret 是要保存在服务器端的,这个 secret 一旦泄露给客户端,客户端就可以自己生成 JWT,并通过该 JWT 访问资源,因此 secret 是永远不该泄露的。
最后将 Header、Payload、Signature 三者用点(.)拼接在一起,就是一个合法签发的 JWT 字符串。在 JWT 官网:https://jwt.io/ 有提供现成的 JWT 签发工具,可以使用该工具来看产生的 JWT 字符串长什么样:
我使用上面我写的三个部分的内容各自填上去,左边就会出现正确的 JWT 字符串出来。红色部分就是编码后的 Header、紫色部分就是编码后的 Payload,蓝色部分就是将 Header、Payload、Secret 合在一起并进行加密算法加密后的编码。最后就是用点(.)将三个部分串联在一起。
客户端如何用 JWT 来访问资源?
-
前端会先通过访问后端的登录 API,后端验证用户账号密码成功后,就会发放合法 JWT 字符串。 -
前端拿到 JWT 字符串就会将 JWT 存放在 Local Storage 里面。 -
而后当前端要访问受保护的资源 API 时,只要在 Header 的填写以下内容:
Authorization: Bearer
-
后端收到后,会去检查 Authorization 的 JWT token 是否有效,如果有效,则允许前端访问受保护的资源。在以前的 Session 的设计上,Session 会存放在 Redis 等这种缓存数据库,每当用户访问受保护的资源时,会先去存储数据库的 Session 进行比对,有效则让用户访问,以 JWT 的方式可以降低查询数据库的需求。
JWT 的优缺点
优点:
-
采用 JSON 对象的形式,大部分的编程语言都支持 -
可以存放一些用户信息,但并非是敏感的信息 -
整个 JWT,只要 Payload 不要放过多的信息,其实 Size 是相当小的 -
不用在 Server 的数据库存放 Session,特别适合多台 Server 的场景下,使得扩展性容易,因为多台 Server 要使用 Session 的话,会有共享 Session 的问题发生 -
对于现在手机上的 APP 的应用特别好,用户不用每次打开 APP 都要重新输入账号与密码 -
支持跨域请求,不会有传统用 Cookie 进行跨域请求等的问题
缺点:
-
JWT 没办法主动失效,也就是说不能像 Session 一样被强制无效,但是个人觉得这有很多方式可以避免 -
JWT 一旦泄露会有很严重的安全性问题,但是泄露通常可以通过两种方式:
-
黑客使用你的电脑,并得知 JWT 这...你电脑都被攻陷了...那就不好说了 -
使用中间人攻击的方式,截取客户端传服务器端的数据包,并获取 JWT,但使用 HTTPS 传输可以大幅度降低该攻击,只要定期更换 SSL 证书就可以了
总结
JWT 之所以会兴起,除了因为 RESTful 架构出现,加上现在微服务的架构的关系,一般来说上线的系统,不太可能用单台服务器来处理一切,多台服务器处理 Session 会有其局限性,虽然可以用统一的数据库进行存放 Session 来控制,但是会有性能的问题。
不过其实这还有很多探讨的空间,在这篇文章没有去说,Session/Cookie 的架构其实也是可以跟 JWT 并行。但就我两者都使用过的经验来看,我觉得 JWT 的方式的确很简洁易懂,Session 的机制有时候的确很繁琐。