JSON Web Token(JWT)机制学习

零、先从传统身份认证说起

HTTP是一种无状态的协议,也就是它并不知道谁是访问应用。把用户看成是客户端,客户端使用用户名还有密码通过了身份认证,不过下回这个客户端再发送请求的时候,服务端还得再验证一下,这就很麻烦。

对于这个情况的解决方法是:
当用户请求登录的时候,如果没有问题,我们在服务器端生成一条记录,这个记录里可以说明一下登录的用户是谁,然后把这条记录的ID号发送给客户端,客户端收到以后把这个ID号存储在cookie里,下次这个用户再向服务器发送请求的时候,可以带着这个cookie,这样服务端会验证一个这个cookie里的信息,看看能不能在服务器端这里找到对应的记录,如果可以,说明用户已经通过了身份验证,就把用户请求的数据返回给客户端。

其实这个解决方法就是session,我们需要在服务端存储为登录的用户生成的session,这些session可能会存储在内存、磁盘或者数据库里。我们需要定期清理过期的session。

这种传统身份认证的问题是:

  1. Session:每次认证用户发起请求时,服务器需要去创建一个记录才存储信息。当越来越多的用户发请求时,内存的开销也会不断增加。
  2. CORS(跨域资源共享):当我们需要让数据跨多台移动设备上使用时,跨域资源的共享会是一个问题。在使用Ajax抓取另一个域的资源,就可以会出现禁止请求的情况。
  3. CSRF(跨站请求伪造)攻击:用户在访问银行网站时,他们很容易受到跨站请求伪造的攻击,并且能够被利用其访问其他的网站。

一、Token的身份认证机制

这种身份认证的好处就是在服务器中不需要存储用户的登录记录。而且具有无状态、可扩展的特点。在客户端存储的tokens是无状态的,并且能够被扩展。基于这种无状态和不存储的session信息,负载均衡器能够将用户信息从一个服务器传到其他服务器上。
并且token身份认证是在请求中发送token而不再是发送cookie,这样做能够防止CSRF(跨站请求伪造)。即使在客户端使用cookie存储token,cookie也仅仅是一个存储机制而不是用于认证。

二、Token身份认证的大概流程

  1. 客户端使用用户名和密码请求登录
  2. 服务端收到请求,然后对用户名和密码进行验证
  3. 验证成功后,服务端会签发一个Token(令牌,具有唯一性),再把这个Token发送给客户端
  4. 客户端收到Token以后可以把它存储起来,比如放在cookie里或者Local Storage里
  5. 客户端每次向服务端请求资源的时候需要带着服务端签发的Token
  6. 服务端收到请求,然后去验证客户端请求里面带着的Token,如果验证成功,就向客户端返回请求的数据

三、JSON Web Token(JWT)机制

JWT是一种紧凑且自包含的,用于在多方传递JSON对象的技术。传递的数据可以使用数字签名增加其安全行。可以使用HMAC加密算法或RSA公钥/私钥加密方式。

紧凑:数据小,可以通过URL、POST参数,请求头发送。且数据小代表传输速度快。
自包含:使用payload数据块记录用户必要且不隐私的数据,可以有效的减少数据库访问次数,提高代码性能。

JWT一般用于处理用户身份验证或数据信息交换

用户身份验证(鉴权):一旦用户登录,每个后续请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是当今广泛使用JWT的一项功能,因为它开销很小,并且能够轻松的跨不同域使用。
数据信息交换:JWT是一种非常方便的多方传递数据的载体,因为其可以使用数据签名来保证数据的有效性和安全性。例如使用公钥私钥,可以确定发件人是他们自称的人。此外,由于使用标头和有效载荷计算签名,还可以验证内容是否未被篡改。

四、JWT的数据结构

JWT的数据结构是:A.B.C
由字符 . 来分隔三部分数据。
A - header头信息(类似请求头)不能传空数据
B - payload有效荷载(记录用户的有效信息,避免多次访问数据库) 可以传空数据,但一般建议包含
C - Signature签名(header+payload+密匙 组成的一个密文数据) 不能传空数据


header(头信息)
大体上是一个JSON数据对象,有两部分组成,常用的散列算法以及令牌的类型。

数据结构:{"alg":"加密算法名称","typ":"JWT"}

alg是加密算法定义内容,如HMAC SHA256 或 RSA
typ是token类型,这里固定为JWT

header部分的JSON被Base64Url编码,形成JWT的第一部分。


payload(有效荷载)
在payload数据块中一般用于记录实体(通常为用户信息)或其他数据的。主要分为是三个部分:已注册声明(registered claims),公开声明(public claims),私有声明(private claims)
payload中常有信息有:iss(发行者),exp(到期时间),sub(主题),aud(受众)等,这些都是已注册声明。
数据结构:{"sub":"125767","name":"DAMN","admin":true}
公开声明部分一般都会在JWT注册表中增加定义,但是为了避免和已注册信息冲突,应在IANA JSON Web令牌注册表中定义它们,或将其定义为包含防冲突命名空间的URI。
私有声明是为了同意使用既没有登记,也没有公开声明各方之间的共享信息而创建的定制声明。
公开声明和私有声明可以由程序员任意定义。

payload部分的JSON被Base64Url编码,形成JWT的第二部分。

注意:即使JWT有签名加密机制,但是payload内容都是明文记录,除非记录的是加密数据,否则不排除泄露隐私数据的可能,不推荐在payload中记录任何敏感数据。


signature(签名信息)
签名信息,是由一个开发者提供的信息,是服务器验证的传递的数据是否有效安全的标准。在生成JWT最终数据之前,先使用header中定义的加密算法,将header和payload进行加密,并且使用 . 进行连接。用来验证发送请求者的身份,由前两部分加密形成,要创建前面部分,必须采用编码标头,编码有效载荷,秘钥,标头中指定的算法并签名。如:加密后的header与加密后的payload,在使用相同的加密算法,对加密后的数据和signature进行加密。得到最终结果。

五、JWT的工作原理

在身份验证中,当用户使用他们的凭证成功登录时,JWT将被返回并且必须保存在本地(通常在本地存储中,也可以使用cookie)。无论何时用户想要访问受保护的路由或资源,用户代理都应该使用承载方案发送JWT,通常在请求头中的Authorization字段,使用Bearer schema:Authorization:Bearer <token>
这是一种无状态身份验证机制,因为用户状态永远不会保存在服务器内存中。服务器受保护的路由将在授权头中检查有效的JWT,如果在服务端存在,则允许用户访问受保护的资源。由于JWT是独立的,所有必要的信息都在那里,减少了多次查询数据库的需求。
ps:JWT是基于浏览器来开发的

六、常见问题

1.JWT安全吗?
答:Base64编码的方式是可逆的,也就是透过编码后发放的Token内容是可以被解析的。一般不建议在payload内放敏感信息。
2.JWT payload内容可以被伪造吗?
答:JWT其中的一个组成内容为Signature,可以防止通过Base64可逆方法回推payload内容并且将其修改,因为Signature是经过header和payload一起Base64编码组成的。
3.如果cookie被窃取了,那么第三方可以做CSRF攻击吗?
答:是的,cookie丢失,就表示身份可以被伪造,官方建议的使用方式是存放在LocalStorage中,并放在请求头中发送。
4.空间及长度问题?
答:JWT Token通常长度不会太端,特别是无状态令牌(Stateless JWT Token),可如果把所有的数据都编在Token里,很快的就会超过Cookie的大小(4K)或者是URL长度限制。
5.Token的失效问题?
答:无状态JWT令牌发放出去之后,不能通过服务器端让令牌失效,必须等到过期时间才会失去效用。假设在这之间Token被拦截,或者有权限管理员身份的差异造成授权Scope修改,这也不能阻止发出去的Token失效以及要求使用使用者重新请求新的Token。

七、对JWT机制的使用建议

  1. 不要存放敏感信息在Token里;
  2. payload中的exp(有效时间)时效不要设定太长;
  3. 开启Only Http以预防XSS攻击;
  4. 在你的应用程序应用层中增加黑名单机制,必要的时候可以进行Block做阻挡。

猜你喜欢

转载自blog.csdn.net/laobanhuanghe/article/details/103588359
今日推荐