一、简介
JSON Web令牌是一种开放的、行业标准的RFC 7519方法,用于安全地表示双方之间的声明。(来自官网翻译)
特点:
- 无状态:无需对会话进行额外的存储方案选择,适合分布式情景下使用
- 非对称加密:通过密钥进行加密前后比较,防止暴力破解
- 携带信息:通过负载携带部分常使用的信息,减少后端与DB的交互
二、使用流程
- 使用用户名与密码请求服务器
- 服务器验证用户信息
- 通过验证后,根据选定的加密方法、负载、密钥,产生一个token并返回给客户端
- 客户端接收到token后进行存储,并在后续的请求中携带该token
- 服务端在处理请求之前,对该token进行获取与验证,如果无误,继续处理请求;如果验证失败,则中止请求并返回特定信息
三、组成结构
通过英文句号.
连接,由三部分组成:header(头部)
、payload(负载)
、signature(签证)
3.1 header(头部)
头部的原始信息类似下面形式的JSON:
{
"typ": 'JWT', //类型申明,
"alg": "HS256" //加密算法
}
上面的信息通过base64加密
后,得到token中的第一个字符,即header
3.2 payload(负载)
负载也是由JSON信息通过base64加密
得到的,此处可以添加自定义参数,如用户名、昵称等经常需要使用的信息(注意base64
为对称加密,所以不要存放敏感信息于此)。
负载也有标准字段
,常见的有如下:
- iss: 签发者
- iat: 签发时间(如无定义则为现在机器时间)
- exp: 过期时间,这个过期时间必须要大于签发时间
nodejs中的jsonwebtoken模块通过sign
方法进行签名时,对于iat
与exp
的使用操作如下:
/**
* ./sign.js
*/
module.exports = function (payload, secretOrPrivateKey, options, callback) {
......
var timestamp = payload.iat || Math.floor(Date.now() / 1000); //iat无定义时,默认当前时间(秒)
......
if (typeof options.expiresIn !== 'undefined' && typeof payload === 'object') {
try {
payload.exp = timespan(options.expiresIn, timestamp);
}catch (err) {
return failure(err);
}
if (typeof payload.exp === 'undefined') {
return failure(new Error('"expiresIn" should be a number of seconds or string representing a timespan eg: "1d", "20h", 60'));
}
}
......
}
上面代码中的timespan
函数引用同项目中的文件./lib/timespan.js
:
可以看到,如果expiresIn
如果为string
类型的数字,如"60"
,则会当作秒数,与iat一起计算得到过期时间;如果expiresIn
为Number
类型,则会当作毫秒数(millisecond)计算过期时间。所以就有了官方Readme中的这句话:
3.3 签证(signature)
通过前面经过base64加密
后形成的header
与payload
,以及密钥secret
与编码方式(默认utf-8)进行非对称加密:
该处引用了jws包,其函数定义的地方如下:
jwsSign
函数定义如下
由上述代码可以了解到,生成token的步骤是:
header
和payload
分别进行base64加密,将两个结果用点号.
连接,得到securedInput
- 通过定义
alg
所对应的加密方法,利用密钥secretOrKey
对securedInput
进行非对称加密得到signature
- 将
securedInput
与signature
再次通过点号.
进行连接,即将header
、payload
、signature
进行连接
四、验证原理
理论上,知道了token的生成方法,大致也就能反推到其验证的原理:
- 先将
header
与payload
通过非对称加密后,得到新的signature
,与token中的原始signature
相比较,如果不等,返回无效token - 如果相等,通过
base64解密
,验证payload
中的过期时间等信息,如果存在过期等异常情况,则也返回无效token(只是与前面无效token的返回信息不同罢了) - 如果一切正常,返回
payload
等信息
为了证明我们猜想的正确性,还是通过观察jsonwebtoken源码来验证,这里就不一张一张的贴图了,将相关代码整合在一起的情况如下:
由上面的过程可以看到,验证的原理基本如猜想一样,验证后,返回解析后的payload
等信息:
自此,token验证的流程完毕。