JWT —— Json Web Token
前言
JWT,可能很多读者听过,或者了解过,也可能工作的时候用到过。
其全称是Json Web Token,具体的中文翻译,我在网络上也没找到比较好的说法,最多就是把token翻译为较为常用的“令牌”一词。
说到JWT,不得不提session。
由于HTTP请求是无状态的,打个比方,比如你使用同一个电脑,同一个浏览器,打开淘宝,点击将商品放入购物车中,然后点击购物车。
在你的视角中,将商品放入购物车,然后点击购物车,都是“你”的行为,这些操作都是“你”做的。但是,服务器是怎么知道,两次请求是“你”做出来的呢?
那自然需要前端与后端进行配合,识别出这两次操作,是又“你”做出来。如果不识别出来你的身份,那点击购物车查看的时候,说不定会看到别人的购物车。那就乱套了啊!
所以,才会出现session与cookie,以此来解决这个问题。
传统的session认证
我们在服务器,使用session技术,将用户的认证信息存在服务器中,并且在HTTP请求响应时,将其部分信息返回,并告诉浏览器将其保存在cookie中。
下次客户端(或者叫前端)再发送请求时,带上cookie,服务器就能根据其中的信息,找到session中对应保存的用户信息,服务器自然就知道用户是“你”,并且把你的购物车信息返回。
session确实也是很不错的解决办法,但是其存在一定的缺陷:
1. 服务器负载量
session是保存在服务器端的,虽然在小型应用中,用户的信息量不大,并且用户数量页不够多。
但是当应用的规模变大时,用户的信息量变大,用户数量也增加,自然服务器的负载就大了。
服务器程序运行时,大多数的数据都是存储在内存中,更何况是用户信息这种需要快速查找的数据。一旦存储总量变大,内存就会告急,影响服务器程序的运行。
2. 扩展性
如果服务器使用分布式的微服务,那么就要保证分布式服务器中,每个服务器上的用户数据具有一致性,这样就需要相应的负载均衡器,并保证多个服务器上的数据同步。
这样也会增加一定的负担
3. CSRF
CSRF全称为 Cross-site request forgery,翻译为跨站请求伪造。
通俗点说就是网站先诱骗用户登录一些需要认证的网站,获取了对应的cookie后,通过该cookie仿造用户的身份,和服务器进行交互,以此实现攻击。
可以说这是使用session与cookie的一种缺点,但其实际上通过一定的防御措施,是可以避免的,不过在本文就不赘述了,感兴趣的读者可以自己去搜索了解。
总结
session与cookie技术是用来解决认证问题的一种方法,其从提出到现在,虽然有些缺陷或者不足,但经过这么多年的实践以及改进,可以说已经是非常的成熟了。
不过由于我们目前的项目前端是小程序,没有cookie(虽然cookie与JWT并不冲突),再加上学长们之前用的就是JWT的方式,所以我们的项目就用了JWT的方式。
JWT
什么是JWT
前面说了那么多,那么到底什么是JWT呢?
JSON Web Token (JWT) is an Internet proposed standard for creating data with optional signature and/or optional encryption whose payload holds JSON that asserts some number of claims.
—— wikipedia
其是一种互联网提议的标准,用于创建具有可选签名和可选加密的数据,其“有效负载”部分持有Json,并断言了一些声明。
可能读者看完上面的说法, 还是没太搞明白。其通俗地讲,就是一种规范,让服务器按照其规范来进行产生token,并将其返回给客户端。
当客户端再次请求时,携带该token,服务器获取到token后,通过规定好的算法来解密,获取用户信息。
也就是说其与session最大的不同,就是session将用户信息,保存在了服务器,每次客户端请求时,以cookie中的数据作为“索引”,在服务器session中查找对应的信息。
而JWT则是直接将用户的部分信息放入到token中,并通过一定的流程进行加密,避免被人恶意解析。在客户端请求登录后,服务器将token返回,让客户端保存。在后来的请求中,客户端携带上token,服务器从token中直接可以把用户信息取出来。总而言之,就是用户信息不在服务器中保存了,而是保存在这个token中,减轻了服务器存储的压力。
那么很显而易见的一个问题就是,token可以被显而易见的取出来,正如cookie一样。因此我们在生成token时,要使用对称的加密算法,进行加密。既然能被获取到已然是事实了,那我们就只能通过不让别人解析有用信息的方法,防止信息泄漏。
JWT的流程
- 用户使用用户名密码(或者小程序的openid)来请求服务器
- 服务器验证用户的信息
- 服务器通过验证,并发给用户一个token
- 客户端存储token,在每次请求时,都带上token
- 服务器验证token并从中取出有用的信息
JWT的构成
JWT包含了三部分:header、payload 以及 signature
header(头部)
header包含了两部分内容:
- 声明类型,这里是jwt
- 声明加密的算法,通常直接使用HMAC SHA256
完整的头部是下面这样的
1 |
|
然后将头部使用base64加密,得到第一部分header:
1 |
|
payload(负载)
负载就是存放信息的地方,其中包括了:
- 标准中注册的声明
- 自定义的声明
标准中的声明有(建议但是不强制使用):
- iss: jwt签发者
- sub: jwt所面向的用户
- aud: 接收jwt的一方
- exp: jwt的过期时间,这个过期时间必须要大于签发时间
- nbf: 定义在什么时间之前,该jwt都是不可用的.
- iat: jwt的签发时间
- jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
自定义的声明:
公共声明可以添加任何信息,一般是放一些不太敏感的信息,该部分实际上也是明文。
1 |
|
使用base64加密后,得到了第二部分payload:
1 |
|
signature(签名)
JWT的签名是header与payload在经过base64加密后,拼接在一起,然后使用声明的加密方式,进行加盐secret加密,就构成了签名
1 |
|
最终将三个部分拼接,并使用 . 进行间隔,就生成了JWT的token
思考
从上面的文字中,可以看出,实际上JWT除了最后的signature,其余部分都可以说是明文了。
我在网络上又找到一篇文章《别再使用JWT作为session系统》,其思路说的也有道理,虽然目前使用JWT比较流行,但是其确实存在一些问题,其作为一次性的授权令牌就是比较好的应用。
同时JWT的思路也值得借鉴,那就是将信息发给用户,让用户保存,在后续请求中带上该信息,即可减轻服务器的存储压力。
我们目前的项目,需要保存用户的一些关键信息,并使用其作为索引,以查找数据库中用户更具体的信息。
直接使用JWT是存在问题的,我们项目的初衷是想隐藏用户id之类的敏感信息,不使其以明文的形式暴露在HTTP请求中,JWT中payload几乎可以说是明文的,不符合要求。
不过可以借鉴JWT的思路,同时再使用对称加密算法,将信息加密,避免了明文传输。
看了不少博客以及文章之后,我们项目最终确定使用加密token来保存信息,并且将信息发送给客户端,让客户端保存。时序图如下:
其他再具体的细节就不方便说了,因为实验室的项目和别人是有合作的,因此就不便透露了。
目前来说,效果还可以,避免用户个别信息明文传输的目标的确是实现了。
参考
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!