之前只是了解JWT是对cookie和session的一种升级方案,在前后分离的项目中用的比较多,今天突然好奇,它到底怎么解决session共享的问题的,觉得大概明白后,总结了一下相关知识。
session数据默认是保存在服务器内存中的,而随着认证用户的增多,服务端的开销会明显增大。
因为session是保存在服务器内存中的,在分布式的应用上,不能保证每次都请求到同一台服务器,相应的限制了负载均衡的能力,只能通过服务器之间的session共享解决问题。
session是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到**跨站请求伪造(CSRF)**的攻击。
Token由三部分组成:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.UQmqAUhUrpDVV2ST7mZKyLTomVfg7sYkEjmdDI5XF8Q
第一部分为头部(header),其中包含了签名的加密算法
第二部分为载荷(payload, 类似于飞机上承载的物品),用来保存用户的相关信息
第三部分是签名(signature),用来验证token是否被篡改过
jwt的头部承载两部分信息:
完整的头部就像下面这样的JSON:
{ 'typ': 'JWT', 'alg': 'HS256'}
然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
包含三个部分:
标准中注册的声明 (建议但不强制使用) :
iss
: jwt签发者sub
: jwt所面向的用户aud
: 接收jwt的一方exp
: jwt的过期时间,这个过期时间必须要大于签发时间nbf
: 定义在什么时间之前,该jwt都是不可用的.iat
: jwt的签发时间jti
: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。公共的声明 :
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息。但不建议添加敏感信息,因为该部分在客户端可解密。
私有的声明 :
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
payload示例:
{ "sub": "1234567890", "name": "John Doe", "admin": true}
然后将其进行base64加密,得到Jwt的第二部分
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
jwt的第三部分token签名,它通过base64加密后的header和payload生成。
服务端将base64加密后的header和payload使用.连接组成一个字符串(头部在前),然后通过header中声明的签名加密方式使用secret密钥加密,就构成了jwt的第三部分。
如果header和payload的内容被修改过,生成的签名就会是不同的(可以理解成不同内容的哈希值不一样),从而达到判断token是否是被篡改过的效果。
签名示例:
UQmqAUhUrpDVV2ST7mZKyLTomVfg7sYkEjmdDI5XF8Q
密钥secret是保存在服务端的,服务端会根据这个密钥进行生成token和验证,需要保护好,不能泄漏。
服务器应用在接受到JWT后,会首先对头部和载荷的内容用同一算法再次进行签名。
如果服务器应用对头部和载荷再次以同样方法签名之后发现,自己计算出来的签名和接受到的签名不一样,那么就说明这个Token的内容被篡改过,否则就完成了验证。
那么服务器应用是怎么知道我们用的是哪一种算法进行的签名的呢?JWT的头部中已经用alg字段指明了我们的签名加密算法了。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。