vlambda博客
学习文章列表

用户登陆 JWT原理剖析 JWT与单点登录实例解释

1. JWT原理剖析


说起用户的登录系统,估计少侠们早就听说过JWT,

那么什么是JWT呢?

为什么应该使用JWT呢?

JWT与Session又有什么关系呢?


我会在下文中详细回答这些问题,在深入学习JWT工作原理之前,我们先来学习一下JWT是干什么用的。


JWT全称JSON Web Token,它的作用是用户授权Authorization,而不是用户的身份认证Authentication,

授权Authorization与认证Authentication在英文上看上去很相似,但是它们却是完全不一样的概念,含义差了十万八千里。


用户认证指的是使用用户名和密码来验证当前的用户身份,简单来说就是用户登录,而用户授权则指用户登录以后,当前用户是否有足够的权限访问特定的资源。


少侠们可以回忆一下,当用户登录失败的时候,返回的错误码应该是401_Unauthorized,未授权,

而当一个用户没有权限读取某一个资源的时候,返回的错误码应该是403_forbidden。


传统上,我们会使用服务器的Session来判断当前用户是否登录,用户登录以后服务器会创建一个用户登录的Session信息,并把Session保存起来,然后把Session的ID通过Cookie传给前端。


当这个用户需要访问某一个API的时候,Session的ID就会被包含在HTTP请求中一起发给后端,后端通过Session ID来判断用户是否已经登录,然后通过用户的权限来判断是否可以给用户提供响应的资源。


因为在服务器中需要保存用户的Session,所以我们称这种登录模式为有状态登录,

但是JWT的出现彻底改变了用户的认证与授权过程,使用JWT来替换浏览器的Cookie,

因为JWT只负责处理授权,所以JWT信息(Token)只需要保存在客户端,而不需要保存在服务器上,从而就可以实现无状态登录,

因为从本质上来说,JWT跟登录根本就没有关系。


接下来,我们快速了解一下使用Session的有状态登录与使用JWT的无状态登录有什么不一样?


传统上我们会使用Session和Cookie来保持用户的授权信息。

第一步登录过程,

用户使用用户名和密码来登录系统,

服务器会来验证用户名和密码是否正确。

如果正确,服务器会给这个用户创建一个包含用户登录信息角色权限的一个叫做Session的东西,然后把这个Session保存起来,同时把Session ID以Cookie的形式发送给前端,表示用户验证成功登录完成了。


接下来,如果用户希望访问某些资源,前端要向后端发起一个HTTP的请求,同时相应的Cookie也会跟随着请求一起发送给服务器,

而服务器取得Cookie以后,就会去查找是否有Session ID,

然后通过Session ID提取相应的Session来确定用户的身份与权限,

如果Session与ID相符,同时Session的用户信息也能提供相应的权限,服务器就会认为这个用户已经登录了,最后资源信息就会通过HTTP响应返回给前端。

图解过程如下:

而相对于JWT来说,这个过程就会简单很多。

第一步同样是用户登录,用户使用用户名和密码登录,如果登录成功,服务器就会返回一个加密文档,这个文档就是JWT,


接下来第二步,用户如果需要访问某些权限的时候,这时候用户就要把JWT放在HTTP请求的Head中,与请求一起发送给服务器,服务器取得JWT以后,

会使用自己的私钥来给JWT文档解密。

图解过程如下:

用户登陆 JWT原理剖析 JWT与单点登录实例解释

如果解密成功,而且数据依然有效,则代表用户已经登录了,

如果JWT所描述的用户权限允许该用户访问资源,那么服务器就会把资源的信息通过HTTP响应发回给前端,

所以少侠们应该可以看出区别了,传统上用户登录状态会以Session的形式保存在服务器上,同时Session ID要保存在前端的Cookie中,


而使用JWT以后,用户的认证信息将会以Token的形式保存在前端,服务器中不需要保存任何用户状态。


这也就是为什么JWT被称作无状态登录,无状态最大的优势就是完美支持分布式部署,你可以使用一个Token发送给不同的服务器,而所有的服务器都会返回同样的结果。


2. JWT与单点登录实例解释


接下来我们将会通过实例详细剖析JWT的原理。


首先我会讲解一下JWT是如何保存用户信息,并且通过非对称加密的方法来进行数字签名的。

我们现在打开一个网站叫做jwt.io,https://jwt.io/是JWT的官网,鼠标往下拉。


在网站的调试部分,这里有两个控制面板:

如图所示:

用户登陆 JWT原理剖析 JWT与单点登录实例解释

左边是JWT编码实例,而右边则是对这个编码的解释。


我们来看看左边编码中的数据,我们可以发现数据被不同的颜色划分为三个部分,红色、紫色以及蓝色,

这三个部分使用小数点来进行分割,分别对应右边面板中相应的部分。

第一个部分是HEADER的,第二部分PAYLOAD,第三个部分VERIFY SIGNATURE,


我们首先看第一个部分红色字的部分,就是JWT的头部HEADER。它具体描述了你当前JWT所使用的编码算法,比如说我们使用的是HS256,这个算法将用于最后一个部分数字签名的验证,中间的紫色部分表示JWT的PAYLOAD,保存的就是具体的用户信息,比如说用户的ID,用户姓名等等,这个部分的字段是可以自定义的,你可以放置任何你想放的数据都没有问题。


我们可以看到有一个叫做iat的字段,是因为issue at的缩写,使用的是一个时间戳,代表的是当前JWT创建的时间。如果你希望给你的Token加上过期日期的话,这个字段就非常重要了。


比如说大部分的JWT,除了包含上述几个字段,还会包含exp这个字段,数据类型是毫秒,指的就是在Token创建好以后过多少秒之后会过期,过期的话当然整个Token都会被作废。


而最后一个部分也就是蓝色字的部分,就是SIGNATURE,这是最重要的部分。这个部分就等于是JWT的激光防伪标志,服务器通过这个数字签名来判断你所发的Token是否有效,是否被篡改过?一旦数字签名失败,整个JWT就作废了。


数字签名使用的是非对称算法进行加密 RSA,只能使用服务器的私钥才能解密。所以只要私钥不丢失,基本上可以视作是绝对安全。


下面我用真实数据给大家演示一下Token的解码过程。


这个Token来自我之前做的一个项目,完成本文的学习以后,少侠们也可以实现JWT的登录与注册。


首先我们得拿到Token,在postman中我提前准备好了一条模拟用户登录的POST请求,请求的主体是用户名和密码,点击发送好了,

用户登陆 JWT原理剖析 JWT与单点登录实例解释

用户登陆 JWT原理剖析 JWT与单点登录实例解释

不过我们怎么才能确定Token的真实性呢?请少侠们把鼠标稍微往下滑一点,请少侠们留意第三个部分,数字签名。


这里有一个输入框,

用户登陆 JWT原理剖析 JWT与单点登录实例解释

我们需要把服务器的私钥填写在这里来验证Token的真实性,因为找不到私钥,所以现在的数字签名是非法的。

用户登陆 JWT原理剖析 JWT与单点登录实例解释

那么私钥在哪里获取呢?私钥应该保存在服务器中,在我的项目中保存在后端的配置文件上,但是这个私钥涉及到服务器的安全性问题,这里就不方便展示了。


反正只要我们取得私钥以后,粘贴在网站的输入框中,这个时候网站就会告诉我们数字签名验证成功了,

用户登陆 JWT原理剖析 JWT与单点登录实例解释

现在就代表这个JWT是合法的,代表当前这个用户登录完成了,所以请少侠们一定要小心,服务器的私钥一定要保管好,绝对不可以泄露。

如果泄露的话,整个用户的系统都会完蛋。


那么在真实的工作中,我们要不要使用JWT呢?

考虑一下下面这个场景,大家都应该用过学校的学生系统吧,学生登录系统以后可以使用这个系统查课、查成绩选课等等。如果现在学校又新建了一个图书馆系统,而且学校希望这个系统可以无缝衔接学生系统使用同一套账户,在学生系统中登录以后就可以直接进入图书馆系统。

用户登陆 JWT原理剖析 JWT与单点登录实例解释

如果我们使用传统的Session验证的方式,我们必须在学生系统和图书馆系统都各自创建一套Session才可以实现登录,而各自的Session无法形成有效的联系,所以必须在最底层创建一个单点登录系统,

用户登陆 JWT原理剖析 JWT与单点登录实例解释

不管学生访问哪个系统,都会连接到单点登录系统进行Session的验证。


当学生的登录信息验证成功以后,单点登录系统会做一个重定向,

用户登陆 JWT原理剖析 JWT与单点登录实例解释

把流量引导至相应的系统中,这样就可以实现学生系统和图书馆系统的无缝连接。


但是单点登录系统在使用分布式部署的情况下,要考虑不同的服务器之间Session的一致性,从而导致整个架构非常复杂。

用户登陆 JWT原理剖析 JWT与单点登录实例解释

那么是不是没有办法使用Session来做单点登录系统呢?

当然不是,目前市场上能够处理单点登录的框架已经非常成熟了,几乎能满足你所有的需求。

比如说有企业级的付费框架ForgeRock,然后微软的Microsoft AM,也有开源的框架OpenAM, OpenIDM, OpenDJ等等。如果少侠们需要,可以不用重复造轮子直接使用这些框架就可以了。


回到我们的案例中,如果学校在建设学生系统的时候使用的是JWT,

那么想要拓展系统简直就是易如反掌,因为JWT不会保存在服务器上,所以服务器如何部署完全不会影响到JWT的使用。

当学生登录完以后,JWT信息会保存在浏览器中,需要访问学生系统或者图书馆系统的时候,会向服务器发送一条带有JWT的请求。学生系统和图书馆系统只需要使用相同的私钥来进行验证就可以了。

当验证成功就可以通过Token中给用户权限的不同,对不同的用户输出不同的数据。

所以这么听起来JWT还是挺靠谱的,它带给我们的就是方便简单。

首先无状态登录简化了服务器用户验证的流程,同时完美支持分布式部署。

第二使用非对称加密,只要私钥不泄露,可以保证Token是绝对安全的。


不过JWT也有两个非常明显的缺点,

第一JWT 的Token一经发布就无法撤销,因为具有无状态的特征,所以在带来便利的同时也无法被服务器禁用。也就是说如果用户因为自身的某些原因导致Token被黑客窃取,那么黑客就可以使用这个Token来伪装登陆,而服务端对此没有任何办法,只能静静的等待Token过期失效。


第二JWT数据的前两个部分也就是HEADER和PAYLOAD并没有被加密,仅仅是使用了base64来编码而已。


也就是说Token中用户的信息等于是明文在传递了,这样很容易造成用户的信息泄露。

对于第二个问题还是很容易解决的,只要给网站加上SSL(变http为https),那基本上就没有什么安全漏洞了,JWT数据会通过https协议加密以后再进行传输。

然而第二个问题就比较棘手了,因为这涉及到JWT无状态登录的本质,所以说无状态是一把双刃剑,带来了便利的同时也带来了隐患,该如何取舍就要看少侠们自己如何规划项目了。