1. 概述
jwt全称 Json Web Token,强行翻译过来就是json格式的互联网令牌,本质就是, 把用户信息通过加密后生成的一个字符串。
它要解决的问题,就是为多种终端设备提供统一的、安全的令牌格式。
2. JWT的组成
为了保证令牌的安全性,jwt令牌由三部分组成,分别是:
- header:令牌头部,记录了整个令牌的类型和签名算法
- payload:令牌负荷,记录了保存的主体信息,比如你要保存的用户信息就可以放到这里
- signature:令牌签名,按照头部固定的签名算法,对整个令牌进行签名,该签名的作用是:保证令牌不被伪造和篡改
它们组合而成的完整格式是:header.payload.signature,例如:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
eyJzdWIiOiJ7XCJjdXJyZW50VXNlcklkXCI6MjQ4NjMwNDAwNTk0NzgwMTYsXCJjdXJyZW50UGhvbmVOdW1iZXJcIjpcIjE4OTYzNTgwMzk1XCIsXCJjdXJyZW50SWRlbnRpdHlJZFwiOjI0ODYzMDQwMzI3OTEzNDcyfSIsImlzcyI6IkpJVUpJVV9DVCIsImN1cnJlbnRVc2VySWQiOjI0ODYzMDQwMDU5NDc4MDE2LCJjdXJyZW50UGhvbmVOdW1iZXIiOiIxODk2MzU4MDM5NSIsImN1cnJlbnRJZGVudGl0eUlkIjoyNDg2MzA0MDMyNzkxMzQ3MiwiaWF0IjoxNjgzMjcxODcwLCJleHAiOjE3MTQ4MDc4NzB9.
gXhxShwXnK81InnObEhr5MWyaTnTCKWwMgXTyh2XB6k
在此故意在“.”后面进行分行
2.1 header
它是令牌头部,记录了整个令牌的类型和签名算法
它的格式是一个json对象,如下:
{
"alg":"HS256",
"typ":"JWT"
}
- alg:signature部分使用的签名算法,通常可以取两个值
1.HS256:一种对称加密算法,使用同一个秘钥对signature加密解密
2.RS256:一种非对称加密算法,使用私钥加密,公钥解密 - type:整个令牌的类型,固定写JWT即可
设置好了header之后,就可以生成header部分了,具体生成方法就是把header部分使用base64 url编码即可
base64 url 不是一个加密算法,而是一种编码方式,它是在base64 算法的基础上对+、=、/三个字符做出特殊处理的算法;
而 base64 是使用64个可打印字符来表示一个二进制数据
2.2 payload
这部分是jwt的主体信息,它仍然是一个JSON对象,它可以包含以下内容:
{
"ss":"发行者",
"iat":"发布时间",
"exp":"到期时间",
"sub":"主题",
"aud":"听众",
"nbf":"在此之前不可用",
"jti":"JWT ID"
}
以上属性可以全写,也可以一个不写,它只是一个规范,就算写了,也需要我们在将来验证这个jwt令牌时手动处理才能发挥作用。
上述属性表达的含义分别是:
- ss:发行该jwt的是谁,可以写公司名字,也可以写服务名称
- iat:该jwt的发放时间,通常写当前时间的时间戳
- exp:该jwt的到期时间,通常写时间戳
- sub:该jwt用于干嘛的
- aud:该jwt是发放给哪个终端的,可以是终端类型,也可以是用户名称,随意一点
- nbf:一个时间点,在该时间点到达之前,这个令牌是不可用的
- jti:jwt的唯一编号,设置此项的目的,主要是为了防止重放攻击(用户使用之前的令牌发送到服务器,被服务器正确的识别,从而导致不可预期的行为发生)
除此之外,我们可以加入任意的自定义信息,当用户登陆成功之后,我们可能需要把用户的一些信息写入到jwt令牌中,比如用户id、账号等等
2.3 signature
这一部分是jwt的签名,正是它的存在,保证了整个jwt不被篡改。
这部分的生成,是对前面两个部分的编码结果,按照header部分指定的方式进行加密,当然我们需要指定一个秘钥。
即:签名=HMACSHA256(base64UrlEncode(header)+“.”+base64UrlEncode(payload),secret)
由于签名使用的秘钥保存在服务器,这样一来,客户端就无法伪造出签名,因为它拿不到秘钥。换句话说,之所以无法伪造jwt,就是因为第三部分的存在。
而前面两部分并没有加密,只是一个编码结果而已,可以认为几乎是明文传输
这不会造成太大的问题,因为,既然用户登录成功了,它当然有权利查看自己的用户信息,
甚至在某些网站,用户的基本信息可以被任何人查看
我们要保证的是,不要把敏感的信息存放到jwt中,比如密码、身份证号等
jwt的signature可以保证令牌不被伪造,那如何保证令牌不被篡改呢?
比如,某个用户登陆成功了,获得了服务器返回的jwt,但他人为的篡改了 payload,比如把自己的账户余额修改为原来的两倍,然后重新编码出 payload 发送到服务器,服务器如何得知这些信息被篡改过了呢?
这就说到令牌的验证了
3. 令牌的验证
令牌在服务器组装完毕后,会以任意的方式发送到客户端
客户端会把令牌保存起来,后续的请求会将令牌发送给服务器
而服务器需要验证令牌是否正确,如何验证呢?
就是对 header+payload 用同样的秘钥和加密算法进行重新加密,然后把加密的结果和传入jwt的 signature 进行对比,如果完全相同,则表示前面两部分没有动过,就是自己颁发的,如果不同,肯定是被篡改过了。
当令牌验证为没有被篡改后,服务器可以进行其他验证:比如是否过期、听众是否满足要求等等,这些就视情况而定了
注意:这些验证都需要服务器手动完成,没有哪个服务器会给我们进行自动验证,当然,我们可以借助第三方库完成这些操作
4. 总结
- jwt本质上是一种令牌格式。它和终端设备无关,它只是规范了令牌的格式而已
- jwt由三部分组成:header、payload和signature。主体信息在payload
- jwt难以被篡改和伪造。这是因为有第三部分的签名存在