通俗易懂讲解Json Web Token (JWT)
作者:scluis
前言
之前只是了解JWT是对cookie和session的一种升级方案,在前后分离的项目中用的比较多,今天突然好奇,它到底怎么解决session共享的问题的,觉得大概明白后,总结了一下相关知识。
基于session认证所显露的问题
session数据默认是保存在服务器内存中的,而随着认证用户的增多,服务端的开销会明显增大。
因为session是保存在服务器内存中的,在分布式的应用上,不能保证每次都请求到同一台服务器,相应的限制了负载均衡的能力,只能通过服务器之间的session共享解决问题。
session是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到**跨站请求伪造(CSRF)**的攻击。
组成
Token由三部分组成:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.UQmqAUhUrpDVV2ST7mZKyLTomVfg7sYkEjmdDI5XF8Q
第一部分为头部(header),其中包含了签名的加密算法
第二部分为载荷(payload, 类似于飞机上承载的物品),用来保存用户的相关信息
第三部分是签名(signature),用来验证token是否被篡改过
header
jwt的头部承载两部分信息:
- 声明类型,这里是jwt
- 声明签名的加密算法 通常直接使用 HMAC SHA256
完整的头部就像下面这样的JSON:
{ 'typ': 'JWT', 'alg': 'HS256'}
然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
playload
包含三个部分:
- 标准中注册的声明
- 公共的声明
- 私有的声明
标准中注册的声明 (建议但不强制使用) :
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
signature
jwt的第三部分token签名,它通过base64加密后的header和payload生成。
服务端将base64加密后的header和payload使用.连接组成一个字符串(头部在前),然后通过header中声明的签名加密方式使用secret密钥加密,就构成了jwt的第三部分。
如果header和payload的内容被修改过,生成的签名就会是不同的(可以理解成不同内容的哈希值不一样),从而达到判断token是否是被篡改过的效果。
签名示例:
UQmqAUhUrpDVV2ST7mZKyLTomVfg7sYkEjmdDI5XF8Q
密钥secret是保存在服务端的,服务端会根据这个密钥进行生成token和验证,需要保护好,不能泄漏。
验证过程
服务器应用在接受到JWT后,会首先对头部和载荷的内容用同一算法再次进行签名。
如果服务器应用对头部和载荷再次以同样方法签名之后发现,自己计算出来的签名和接受到的签名不一样,那么就说明这个Token的内容被篡改过,否则就完成了验证。
那么服务器应用是怎么知道我们用的是哪一种算法进行的签名的呢?JWT的头部中已经用alg字段指明了我们的签名加密算法了。
基于token的鉴权机制
- 用户使用用户名密码来请求服务器
- 服务器进行验证用户的信息
- 服务器通过验证发送给用户一个token
- 客户端存储token,并在每次请求时附送上这个token值
- 服务端验证token值,并返回数据
优点
- 支持跨域访问: Cookie是不允许垮域访问的,Token机制不基于Cookie实现,可以跨域访问。
- 服务端无状态,可扩展性好: Token机制在服务端不需要存储session信息,因为Token 自身包含了所有登录用户的信息,服务端只需要进行token验证即可。
- 更安全:Token不基于Cookie实现,不会出现Cookie劫持所导致的安全问题。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。