JWT的原理,深入浅出JWT(JSON Web Token)

技术相关 2019-11-06T10:04:22 浏览:44

一、一般认证的问题

互联网服务离不开用户认证,认证也就是某个用户拥有某些事物的权利,比如你有你家的钥匙,你可以打开你的家门,你如果有你朋友家的钥匙你也可以打开你朋友的家门,能打开这个门,关键就看你有没有钥匙。一般流程就像下面这样。

1.1.用户向服务器发送用户名和密码(匹配生成钥匙)。
1.2.服务验证通过后,在当前对话(session)里面保存相关数据(家里),比如用户角色、登录时间等等 。
1.3.服务器向用户返回一个session_id(钥匙),写入用户的Cookie(用户随身携带)。
1.4.用户随后的每次请求操作,都会通过Cookie,将session_id(钥匙)传回服务器。
1.5.服务器收到session_id,找到前期保存的数据(家里),由此得知用户的身份。

这种模式的问题在于不适合服务器集群,或者是跨域的服务,因为session保存的是某台服务器上,单机没有问题,多个服务器就会受到限制。当然这种模式也有解决方案。

二、JWT 的原理

JWT的原理是,服务器认证以后,生成一个Encodeed编码返回给用户,就像下面这样。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJtbGEiLCJuYW1lIjoibWxhIiwiaWF0IjoxNTE2MjM5MDIyfQ.751hRmwqVjzXt4Omxn3yKZdrvLK6ILrBU1Rm41Kyb-I

以后,用户与服务端通信的时候,都要发回这个Encodeed编码。服务器完全只靠这个Encodeed编码认定用户身份。为了防止用户篡改数据,服务器在生成这个Encodeed编码的时候,会加上签名(涉及到密码学的知识,大家可以了解)。

服务器就不保存任何 session 数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。

三、JWT 的数据组成

它是一个很长的字符串,中间用点(.)分隔成三个部分。

Header(头部).Payload(负载).Signture(签名)

3.1 Header

Header 部分是一个 JSON 对象,描述 JWT 的元数据,通常是下面的样子。

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

上面代码中,alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT。最后将上面的JSON对象使用Base64URL算法转换成字符串。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

3.2 Payload

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。声明有三种类型: registered, public 和 private。

3.2.1 Registered claims : 这里有一组预定义的声明,它们不是强制的,但是推荐。比如:iss (issuer), exp (expiration time), sub (subject), aud (audience)等。
3.2.2 Public claims : 可以随意定义。
3.2.3 Private claims : 用于在同意使用它们的各方之间共享信息,并且不是注册的或公开的声明。

{
  "sub": "mla",
  "name": "mla",
  "iat": 1516239022
}

最后将上面的JSON对象使用Base64URL算法转换成字符串。

eyJzdWIiOiJtbGEiLCJuYW1lIjoibWxhIiwiaWF0IjoxNTE2MjM5MDIyfQ

注意,不要在JWT的payload或header中放置敏感信息,除非它们是加密的。

3.3 Signature

Signature 部分是对前两部分的签名,防止数据篡改。

首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

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

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用”点”(.)分隔,就可以返回给用户。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJtbGEiLCJuYW1lIjoibWxhIiwiaWF0IjoxNTE2MjM5MDIyfQ.OonH2EmXXt1-mrEch_iFLie76BKhGVJDJfWA6aIrptA

四、JWT 的使用方式

客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。以后每次与服务器的通信,都要带上这个JWT,你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。

Authorization: Bearer <token>

备注

签名的目的?

签名的过程,实际上是对头部以及载荷内容进行签名。一般而言,加密算法对于不同的输入产生的输出总是不一样的。对于两个不同的输入,产生同样的输出的概率极其地小。

所以,如果有人对头部以及载荷的内容解码之后进行修改,再进行编码的话,那么新的头部和载荷的签名和之前的签名就将是不一样的。而且,如果不知道服务器加密的时候用的密钥(这个密钥很重要,泄露了它就是泄露了一切)的话,得出来的签名也一定会是不一样的。

服务器应用在接收到JWT后,会首先对头部和载荷的内容用同一算法( JWT的头部alg字段指明的加密算法 )再次签名。

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

总之签名的目的就是防止服务器返回的Token被修改,因为服务器返回的Token是通过服务器加密的,加密的时候用了只有服务器知道的密钥,所以无法伪造这个Token。它是不是正确合法的Token只有服务器知道。

可以到 https://jwt.io/#debugger-io 测试生成Token。