JWT的那些事儿

什么是JSON Web Token?

JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且独立的方式,可以在各方之间作为JSON对象安全地传输信息。此信息可以通过数字签名进行验证和信任。JWT可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。

虽然JWT可以加密以在各方之间提供保密,但我们将专注于签名令牌。签名令牌可以验证其中包含的声明的完整性,而加密令牌则隐藏其他方的声明。当使用公钥/私钥对签署令牌时,签名还证明只有持有私钥的一方是签署私钥的一方。

什么时候应该使用JSON Web令牌?

以下是JSON Web令牌有用的一些场景:

授权:这是使用JWT的最常见方案。一旦用户登录,每个后续请求将包括JWT,允许用户访问该令牌允许的路由,服务和资源。Single Sign On是一种现在广泛使用JWT的功能,因为它的开销很小,并且能够在不同的域中轻松使用。

信息交换:JSON Web令牌是在各方之间安全传输信息的好方法。因为JWT可以签名 - 例如,使用公钥/私钥对 - 您可以确定发件人是他们所说的人。此外,由于使用标头和有效负载计算签名,您还可以验证内容是否未被篡改。

举一个例子,假如一个电商网站,在用户登录以后,需要验证用户的地方其实有很多,比如购物车,订单页,个人中心等等,访问这些页面正常的逻辑是先验证用户权限和登录状态,如果验证通过,则进入访问的页面,否则重定向到登录页。我使用JWT一般都是结合OAuth2.0进行使用。

而在 JWT 之前,这样的验证我们大多都是通过 cookie 和 session 去实现的,我们接下来就来对比以下这两种方式的不同。

由于浏览器的请求是无状态的,cookie 的存在就是为了带给服务器一些状态信息,服务器在接收到请求时会对其进行验证(其实是在登录时,服务器发给浏览器的),如果验证通过则正常返回结果,如果验证不通过则重定向到登录页,而服务器是根据 session 中存储的结果和收到的信息进行对比决定是否验证通过,当然这里只是简述过程。

从上面可以看出服务器种植 cookie 后每次请求都会带上 cookie,浪费带宽,而且 cookie 不支持跨域,不方便与其他的系统之间进行跨域访问,而服务器会用 session 来存储这些用户验证的信息,这样浪费了服务器的内存,当多个服务器想要共享 session 需要都拷贝过去。

JWT 的过程

当用户发送请求,将用户信息带给服务器的时候,服务器不再像过去一样存储在 session 中,而是将浏览器发来的内容通过内部的密钥加上这些信息,使用 sha256 和 RSA 等加密算法生成一个 token 令牌和用户信息一起返回给浏览器,当涉及验证用户的所有请求只需要将这个 token 和用户信息发送给服务器,而服务器将用户信息和自己的密钥通过既定好的算法进行签名,然后将发来的签名和生成的签名比较,严格相等则说明用户信息没被篡改和伪造,验证通过。

JWT 的过程中,服务器不再需要额外的内存存储用户信息,和多个服务器之间只需要共享密钥就可以让多个服务器都有验证能力,同时也解决了 cookie 不能跨域的问题。

JWT 的结构

JWT 之所以能被作为一种声明传递的标准是因为它有自己的结构,并不是随便的发个 token 就可以的,JWT 用于生成 token 的结构有三个部分,使用 . 隔开。

1、Header

Header 头部中主要包含两部分,token 类型和加密算法,如 {typ: “jwt”, alg: “HS256”},HS256 就是指 sha256 算法,会将这个对象转成 base64。

2、Payload

Payload 负载就是存放有效信息的地方,有效信息被分为标准中注册的声明、公共的声明和私有的声明。

(1) 标准中注册的声明

下面是标准中注册的声明,建议但不强制使用。

iss:jwt 签发者;

sub:jwt 所面向的用户;

aud:接收 jwt 的一方;

exp:jwt 的过期时间,这个过期时间必须要大于签发时间,这是一个秒数;

nbf:定义在什么时间之前,该 jwt 都是不可用的;

iat:jwt 的签发时间。

上面的标准中注册的声明中常用的有 exp 和 nbf。

(2) 公共声明

公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息,但不建议添加敏感信息,因为该部分在客户端可解密,如 {“id”, username: “panda”, adress: “Beijing”},会将这个对象转成 base64。

(3) 私有声明

私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为 base64 是对称解密的,意味着该部分信息可以归类为明文信息。

3、Signature

Signature 这一部分指将 Header 和 Payload 通过密钥 secret 和加盐算法进行加密后生成的签名,secret,密钥保存在服务端,不会发送给任何人,所以 JWT 的传输方式是很安全的。

最后将三部分使用 . 连接成字符串,就是要返回给浏览器的 token 浏览器一般会将这个 token 存储在 localStorge 以备其他需要验证用户的请求使用。

注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。

如何应用

一般是在请求头里加入Authorization,并加上Bearer标注:

1
2
3
4
fetch('api/user/1', {
headers: { 'Authorization': 'Bearer ' + token
}
})

服务端会验证token,如果验证通过就会返回相应的资源。整个流程就是这样的:

总结

优点

因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。

因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。

便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。

它不需要在服务端保存会话信息, 所以它易于应用的扩展

安全相关

不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分。

保护好secret私钥,该私钥非常重要。

如果可以,请使用https协议