引言
最近学 jwt
学疯了,我之前很少做梦的,这两天天天做梦。
梦到自己写 jwt
,梦到制定http 3.0
规范,梦到 Github
封杀我。
可能是又到了一层境界。
最近看了好多的博客去研究 jwt
,发现很多博客对jwt
存在误解,所以我特意去 jwt
的官方网站去学习,试图理解设计的思想。
jwt
请移步 jwt
官网。
jwt
:json web token
。
与传统的 SESSION
认证方式不同的是,传统方式是 COOKIE
里面存放 JSESSIONID
,浏览器根据JSESSIONID
找SESSION
,用户信息存放在 SESSION
里。
jwt
则不同,令牌经过解密,得到的就是用户信息,所以完全可以不用SESSION
,以减轻服务器的存储压力。
jwt
由三部分组成:HEADER
、PAYLOAD
、SIGNATURE
。
HEADER
头信息包括签名算法和令牌类型。
如下数据所示,签名算法为HS256
(签名算法会在后面用到),令牌类型为JWT
。
{
"alg": "HS256",
"typ": "JWT"
}
将头信息使用 base64url
加密,注意不是base64
!两者区别:difference-between-basic-and-url-base64-encoding
在线加密网站:base64 encode online
注意,为了方便阅读,将 json
添加换行与缩进,在标准的 jwt
中,需要去掉换行和空格再进行加密。
加密完成:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
,jwt
的头部有了。
PAYLOAD
语言的最高境界就是不用翻译,这里就不翻译了,similar to data
。
这部分比较自由,官方文档是这样描述的:not mandatory but recommended
。
mandatory
大家肯定认识,这个词都用了快两年了。
iss (issuer), exp (expiration time), sub (subject), aud (audience)
{
"iss": "yunzhi_auth_service",
"exp": "1554453501",
"sub": "zhangxishuo1998"
}
对此 json
串进行 base64url
加密。
base64url
说解密就解密,所以不建议在数据中添加敏感信息,总不能把密码放里吧?一个用户 id
足矣,就像我们过去的 SESSION
一样。
如果觉得 id
不合适,那就用用户名,反正保证 unique
和查询效率即可,给 username
这一列加上索引,B+
树查询相当快。
httpSession.setAttribute(TeacherService.TEACHER_ID, persistTeacher.getId());
加密完成:eyJpc3MiOiJ5dW56aGlfYXV0aF9zZXJ2aWNlIiwiZXhwIjoiMTU1NDQ1MzUwMSIsInN1YiI6InpoYW5neGlzaHVvMTk5OCJ9
,jwt
的数据有了。
SIGNATURE
因为数据是透明的,所以假如用户把用户名给改了,再 base64url
加密,再去访问我们的系统,那不就变成了另一个用户登陆了吗?
为了解决数据篡改问题,引入了签名机制。
后台生成一个密钥用于签名,这里密钥以 yunzhi
为例。
数据是HEADER
+ .
+ PAYLOAD
,即:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJpc3MiOiJ5dW56aGlfYXV0aF9zZXJ2aWNlIiwiZXhwIjoiMTU1NDQ1MzUwMSIsInN1YiI6InpoYW5neGlzaHVvMTk5OCJ9
。
使用 HEADER
里设置的 HS256
算法和密钥 yunzhi
对数据进行签名。
这里不知道是不是我用的有问题,我用在线工具签出来的签名一直是错误的。但是我检查了好几遍,我就是和官网相同的步骤。很疑惑。
jwt
官网签出来的签名是KlmSnbhfw8Q2RTB3KmOhFJcFxwzxJEcYa5osOlYP-5U
。
连接
将 HEADER
、PAYLOAD
、SIGNATURE
用.
连接在一起,就是jwt
。
jwt
:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ5dW56aGlfYXV0aF9zZXJ2aWNlIiwiZXhwIjoiMTU1NDQ1MzUwMSIsInN1YiI6InpoYW5neGlzaHVvMTk5OCJ9.KlmSnbhfw8Q2RTB3KmOhFJcFxwzxJEcYa5osOlYP-5U
。
签名
这里一起研究一下签名,签名是防止数据篡改的。
因为签名需要一个密钥,这个密钥是服务器私有的,所以能保证这个签名不被伪造。只有服务器能签名。
校验时,服务器再根据自己的密钥生成签名,对比,如果一致,说明数据没有被篡改。
SSL
其实也是有签名的,其实就是我们常见的 HTTPS
证书。
RSA
非对称加密,公私钥,私钥存储再服务器中,公钥要传输给浏览器。怎么保证传输过程公钥不被篡改呢?
如果公钥被黑客篡改,那就毫无安全性可言。
为了保证公钥的安全,设计了 HTTPS
证书。
CA
机构将公钥进行 hash
,生成摘要,再用自己的私钥对摘要进行签名,即生成数字签名。HTTPS
证书,就是公钥 + 数字签名。
浏览器获取到证书,同样对公钥进行 hash
,生成摘要,用CA
公钥对数字签名解密,如果两者一致,证明证书没有被篡改。
总结
其实学习这个有啥用呢?如果只是停留在 jwt
和https
的使用上,那确实没什么用。
学习的是一种思想,一种前人经过无数次推敲总结出来的设计思想。
如果以后我们再碰到一个防止数据篡改的业务,有了这种思想,我们也可以设计自己的数字签名。