带你弄懂JWT原理

JSON Web Token(JWT)是一个很是轻巧的规范。这个规范容许咱们使用JWT在用户和服务器之间传递安全可靠的信息。php

让咱们来假想一下一个场景。在A用户关注了B用户的时候,系统发邮件给B用户,而且附有一个连接“点此关注A用户”。连接的地址能够是这样的html

https: //your.app.com/make-friend/?from_user=B&target_user=A

上面的URL主要经过URL来描述这个固然这样作有一个弊端,那就是要求用户B用户是必定要先登陆的。可不能够简化这个流程,让B用户不用登陆就能够完成这个操做。JWT就容许咱们作到这点。前端

JWT的组成

一个JWT实际上就是一个字符串,它由三部分组成,头部载荷签名git

{

​ "iss": "John Wu JWT", github

​ "iat": 1441593502, web

​ "exp": 1441594722, 算法

​ "aud": "www.example.com, json

​ "sub": "kevin@example.com", 后端

​ "from_user": "B", 安全

​ "target_user": "A"

}

这里面的前五个字段都是由JWT的标准所定义的。

  • iss: 该JWT的签发者
  • sub: 该JWT所面向的用户
  • aud: 接收该JWT的一方
  • exp(expires): 何时过时,这里是一个Unix时间戳
  • iat(issued at): 在何时签发的

这些定义均可以在标准中找到。

将上面的JSON对象进行[base64编码]能够获得下面的字符串。这个字符串咱们将它称做JWT的Payload(载荷)。

eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiZnJvbV91c2VyIjoiQiIsInRhcmdldF91c2VyIjoiQSJ9
注意:base64是一种编码,它是能够被翻译回原来的样子来的。它并非一种加密过程。
头部(Header)

JWT还须要一个头部,头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。这也能够被表示成一个JSON对象。

{

"typ": "JWT",

"alg": "HS256"

}

在这里,咱们说明了这是一个JWT,而且咱们所用的签名算法(后面会提到)是HS256算法。

对它也要进行Base64编码,以后的字符串就成了JWT的Header(头部)。

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
签名(签名)

将上面的两个编码后的字符串都用句号.链接在一块儿(头部在前),就造成了

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0

最后,咱们将上面拼接完的字符串($input)用HS256算法($algo)进行加密。在加密的时候,咱们还须要提供一个密钥($secret)。比方咱们使用字符串mySecret做为密钥的话,那么就能够经过下面的方法获得咱们加密后的内容做为JWT的签名:

hash_hmac($algo, $input, $secret);// output: rSWamyAYwuHCo7IFAgd1oRpùSP7nzL7BF5t7ItqpKViM

最后将这一部分签名也拼接在被签名的字符串后面,咱们就获得了完整的JWT:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0.rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM

因而,咱们就能够将邮件中的URL改为

https://your.app.com/make-fri...

这样服务端验证完JWT的签名没问题后就能够完成B用户添加A用户为好友的操做而不在须要用户再进行登陆啦。

看到这里你应该会有两个疑问

  1. 签名的目的是什么?
  2. Base64是一种编码,是可逆的,那么个人信息不就被暴露了吗?

签名的目的

最后一步签名的过程,其实是对头部以及载荷内容进行签名。通常而言,加密算法对于不一样的输入产生的输出老是不同的。对于两个不一样的输入,产生一样的输出的几率极其地小。

因此,若是有人对头部以及载荷的内容解码以后进行修改,再进行编码的话,那么新的头部和载荷的签名和以前的签名就将是不同的。并且,若是不知道服务器加密的时候用的密钥的话,得出来的签名也必定会是不同的。

服务器应用在接收到JWT后,会首先对头部和载荷的内容用同一算法再次签名。那么服务器应用是怎么知道咱们用的是哪种算法呢?别忘了,咱们在JWT的头部中已经用alg字段指明了咱们的加密算法了。

若是服务器应用对头部和载荷再次以一样方法签名以后发现,本身计算出来的签名和接受到的签名不同,那么就说明这个Token的内容被别人动过的,咱们应该拒绝这个Token,返回一个HTTP 401 Unauthorized响应。

关于两个签名的推荐用PHP中内建函数hash_equals来帮咱们完成

hash_equals ( string $known_string , string $user_string ) : bool

比较两个字符串,不管它们是否相等,本函数的时间消耗是恒定的。

本函数能够用在须要防止时序攻击的字符串比较场景中, 例如,能够用在比较 crypt() 密码哈希值的场景。

信息会暴露

JWT荷载和头部中的信息均可以经过base64解码拿到,因此在JWT中,不该该在载荷里面加入任何敏感的数据。在上面的例子中,咱们传输的是用户的User ID。这个值实际上不是什么敏感内容,通常状况下被知道也是安全的。像密码这样的内容就不能被放在JWT中了。若是将用户的密码放在了JWT中,那么怀有恶意的第三方经过Base64解码就能很快地知道你的密码了。

JWT的适用场景

咱们能够看到,JWT适合用于向Web应用传递一些非敏感信息。例如在上面提到的完成加好友的操做,还有就是

JWT还常常用于设计用户认证和受权系统,尤为是如今先后端分离开发架构下,用户登陆成功后将携带着用户标示和登陆时间、站点等信息的JWT给到前端,前端在API请求的HEADER头中携带JWT,服务端验证完JWT的合法性后就能经过用户标示找到本次API请求所属的用户。

以前用PHP写了一个很是简陋的生成和校验JWT的程序,能够拿来参考,代码放到gist上了https://gist.github.com/kevin...

相关文章
相关标签/搜索