以JWT 代替传统Token

JSON Web Token (JWT)是由Auth0所提构出的一个新Token想法,这并不是一套软件、也不是一个技术,如果你在做网站时有用Token验证使用者身份的习惯,那么这个方法你应该很快就能上手。我们先来讲一讲为什么JWT会比传统Token要好。

在传统网站中我们会以Session 来判定使用者是否有登入,由于Session 只会被服务端知道,所以我们就可以Session 中存放一些重要数据并且供之后验证用。

 

为什么不用Session了?

随着网络的扩展,Session 有个问题,那就是具状态性(Stateful)还有容易受跨网域请求伪造攻击(CSRF Attack)。 先让我们先以具状态性的问题为例。

 

极具状态性

假设今天有两台服务器通过负载均衡来分派使用者的请求,由于Session是储存在服务端上的,第一次使用者登入时是由Server 1处理,那么这个Session自然也就储存在Server 1 。

但下次负载平衡指派使用者到Server 2 的时候呢? 这个Session也就不存在 ,所以使用者就需要重新登入一次(虽然有办法可解决,但暂不讨论)。

 

易受跨站请求伪造攻击

由于Session是储存在服务端的,这意味着客户端在发送请求时几乎不用提供什么数据,对吧? 如果今天有人发送了一个删除文章的链接给你,然后你在不知情的情况下就按下去的时候会发生什么事情? 你的文章会这样被删除掉。

为此,有人想出了Token 来解决这个问题,并且能够在多个服务器上跨域使用。

 

Token解决了什么?

当使用者登入成功时,他会得到一串看起来毫无意义的乱数字串,但这个字串实际上会对应到数据库中使用者的身份,简单说就是另类的帐号,这也被叫做Token。

 

无状态性

Token本身是不携带数据的且无状态性的(Stateless),当服务器接收到Token时,会主动去数据库中找到使用者的数据表,接着就能够知道这个Token代表着哪个使用者,然后获取相关的数据来使用。

由于多个服务器都是共享一个数据库的,所以我们的服务器现在不会有Session 那样的问题。 但需要注意的是Token 不可以让别人知道,否则别人就能够拥有你的身份、伪造成你。

 

安全性提高

由于使用者现在必须主动提供 Token,也顺带解决了跨网域请求伪造攻击,如果今天朋友再次给了你删除文章的链接,由于这个链接并不带有 Token,服务器也就不能知道是谁想要删除文章,所以这个动作自然就会变成无效。

 

为什么要用JWT 取代传统Token?

你能够直接在JWT 中存放数据,而不用额外的查询数据库。

当我们使用传统Token时,我们需要查询数据库并且比对这一个Token是谁的,然后我们才能够取得该使用者的数据。 当有大量使用者涌入时,这可能会让服务器负荷不堪。

而JWT解决了这个问题,因为我们可以直接把使用者数据存放在「Token」中,所以也就省去了额外的数据库开销。 且在微服务架构中也能够方便地获取使用者数据。

到这里你可能会开始想:「 这样不是很危险吗? 」、「 如果使用者自行修改了Token该怎么办? 」,而我们接下来就要讲一下什么是JWT。

 

什么是JSON Web Token(JWT)?

先看看JWT 的长相吧。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ 

上面是一个来自官方网站范例 ,我们将以此做为讲解。

 

JWT 的构成

JWT 实际上是由三个部分组成的。

标头.内容.签名

标头

 JWT的标头包含了两个部分,一个是加密类型( alg ),另一个则是定义类型( typ )。

 { "alg": "HS256", "typ": "JWT" } 

而这些内容通过Base64转化就能够得到我们的第一段字串。

 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 

内容

内容是一个自定义的地方,你可以在里面存放使用者的帐号、昵称,这样你就不需要再去数据库查询,这听起来很不安全,但是不用担心。

不过需要注意的是这些数据并没有被「加密」,使用者可以直接看到这些数据,所以不推荐在这里摆放信用卡信息、密码。

 { "sub": "1234567890", "name": "John Doe", "admin": true } 

这部分通过Base64转化也就能得到以下字串。

 eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9 

签名

这就是好玩的地方了,当我们要签发一个JWT 的时候,我们会用一组密码来签名,这就是为了避免有人自己更改内容,然后拼凑一个根本不是我们所产生的JWT 来欺瞒服务端。

这部分的算法是由上方两个区块的Base64以点( . )符号组合起来,并以一个字符串(在这里是secret )加密。

 HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), "secret") 

然后我们就会得到签名。

 TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ 

 

接着组合起来就是JWT 本体。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ 

由于secret这个字符串是储存在服务端的,所以也就没有人能够知道(除非暴力破解)。 任何人都可以修改JWT 的内容,但是当他签发的时候并不知道这个字符串,所以就会有不对的签名,服务端也就自然不会接受这个错误的JWT。

使用JWT 不仅能够节省服务端的数据库连接开销,又能够在数据分享上变得更加便利。

版权声明:本文为peng104原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/peng104/p/10621821.html