
什么是 JWT
JWT 全称为 JSON Web Token 是一种在跨域时验证用户的一种措施。
JWT 与 传统 COOKIE SESSION 的区别是什么?
首先我们来回想一下传统 cookie session 的验证流程
- 客户端登陆账号
- 服务端接受到用户信息,验证账号密码,签发一个 sessionId,以 cookie 的方式的返回给客户端存储
- 每当下次请求的时候,客户端就会携带上同域名的 cookie,里面就有 sessionId,以此标识用户身份。
这样会有一个问题,如果 网站A 和 网站B 都是同一家公司。现在要求,网站A 登陆了,网站B 也自动登陆,请问怎么实现?
一种是 Session 持久化,写入数据库或者其他持久层(比如 redis),各种服务收到请求后,都向持久层请求数据,这种模式架构清晰,但是工程量大,且万一持久层失效,单点登陆就会失效。
一种则是服务器直接放弃存储 session 数据了,直接将信息存在客户端。**JWT **就是这种方案的一个代表
JWT 原理
JWT 的原理是,服务器认证以后,生成一个 JSON 对象发送给客户端,大概长这样。
{
"name": 'cjfff',
"role": "admin",
"ita": "认证时间"
}
往后在通信过程中,每次客户端请求都带上,以识别身份。
但是为了防止数据被擅改,会加入签名信息(Signature)。
JWT 的数据结构
组成部分
实际的 JWT 是图示这个样子。

这里对数据进行了手动回车换行,方便大家下面复制对比。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJyb2xlIjoiYWRtaW4iLCJuYW1lIjoiY2pmZmZmIiwiaWF0IjoxNTE2MjM5MDIyfQ
.D7OUQ7tygm9PDjXeroA0w3dV4G0Swcbju0jHkS7Hqww
可以看到分为了三个部分
- header 红色部分
- payload 紫色部分
- signature 蓝色部分
每个部分的信息都是怎么来的?
这里先来个帮助函数
const Base64 = {
encode(str) {
// first we use encodeURIComponent to get percent-encoded UTF-8,
// then we convert the percent encodings into raw bytes which
// can be fed into btoa.
let repMap = {
'=': '',
'+': '-',
'/': '_'
}
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
function toSolidBytes(match, p1) {
return String.fromCharCode('0x' + p1);
})).replace(/(=|\+|\/)/g, (key) => repMap[key])
},
decode(str) {
// Going backwards: from bytestream, to percent-encoding, to original string.
return decodeURIComponent(atob(str).split('').map(function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
}
};
- header 记录着加密算法信息
大概如下,里面记录着 type,alg 就是加密算法, 可以看到,加密后是与开头数据结构中的 header(第一部分是相同的).
Base64.encode(JSON.stringify({
"alg": "HS256",
"typ": "JWT"
})) === 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9' // true
- payload 用户信息以及签发时间
可以看到 payload 信息也是产出一致的。
Base64.encode(JSON.stringify({
"role": "admin",
"name": "cjffff",
"iat": 1516239022
})) === 'eyJyb2xlIjoiYWRtaW4iLCJuYW1lIjoiY2pmZmZmIiwiaWF0IjoxNTE2MjM5MDIyfQ'
- Signature 签名
签名的生成规则如下,这里就不做测试了
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
他们三个部分以 . 组合后就是 JWT 整体数据结构。
JWT 安全吗?
JWT 只是为了解决用户会话的问题,大白话就是识别用户身份。
JWT 本身并没有进行加密的行为,Signature(签名) 也只是为了别人擅改数据而已,如果数据被抓包,payload(jwt第二部分内容)中如果携带用户敏感信息的话,第三方使用 base64 解密即可获得数据,所以为了安全性而言请使用 https 代替 http。
**
另外就是应该防止加密用的 **secret **泄漏。
安全总结:
- 使用 https 代替 http
- 防止 secret 泄漏
- 可以对 JWT 进行二次加密(需要额外消耗)
JWT 如何过期?
**JWT **在签发的时候会为 **payload **加上一个 iat(issued at) 签发时间。
然后客户端下次发送带上发送给服务端的时候会根据服务端的过期策略进行验证
比如,服务端设置的过期时间是 10h
- 服务端接收到 jwt ,解密后获得签发时间
- 如果签发时间 + 10h < 当前时间就是过期了
另外就是,如果用户数据被擅改,此时 JWT 应该是属于过期的,但由于 JWT 的机制问题,token 签发后并没有相关的废弃策略,只能使用额外的逻辑去对过期的 JWT 进行过期操作,比如将过期的 JWT 维护在 黑名单列表中。
JWT 如何使用?
客户端收到服务端返回的 JWT 后,可以存储在 COOKIE 中,也可以存储在 localStorage 中。
当每次发送请求的时候, 在 headers 加上 Authorization 字段,内容为 Bearer ${jwt}
即可
然后服务端会用上述接收到的信息,拿到 header + payload 为输入数据,进行上面算法生成 Signature 再和 客户端接收到的 Signature 进行对比,一致的话就完成了验证。
JWT Koa2 demo
参考
## 延伸阅读
- jsonwebtoken 源码 里面就是根据上述规则去写的逻辑,感兴趣的可以看看