我最近一直在阅读JWT,觉得我对它们是如何制作的以及如何用于身份验证有了相当好的了解。
如果我理解正确的话,当我需要几个web服务(API)时,有状态应用程序就会遇到挑战,每个API用于使用该应用程序的每种设备(web、移动设备等)。然后,web服务必须以某种方式同步会话状态,这很难。
相反,我们将状态客户端(最好是在cookie中)存储在加密和签名的JWT中。
到目前为止我理解对了吗?
然后,我的主要问题是:JWT中到底存储了什么,比如说在线商店?它是否完全替换存储在数据库中的所有用户信息?因此,配置文件信息、图像、购物车、保存的内容(文章、转发等,如果适用)等等。所有这些以及所有其他可以想象的内容是否都保存在JWT中?
总之,我试图弄清楚JWT用例中的无状态是什么意思?我们是否将所有用户信息存储在令牌中?
[..]当我需要几个web服务(API)时,使用应用程序的每种类型的设备(web、移动等)都有一个。
我认为,按客户端类型分离服务器后端是一种糟糕的体系结构。理想情况下,后端将为web客户端和移动客户端(以及任何其他客户端)提供完全相同的API。否则,每一个受支持的功能都会产生巨大的重复和开销。
因此,让我们集中讨论一下您有多个服务器的情况。任何一个严肃的大型网站,比如Amazon.com,都有不止一台服务器为其网站服务。随着需求的增加,它们的单个服务器实例会自动联机,而随着流量的减少,它们会重新脱机。负载平衡器根据需要将流量引导到各个服务器。
在这种情况下,尤其是在购物网站上,您有几种处理状态的方法,每种方法都有一定的优缺点:
-
使用粘性会话,这意味着web服务器是有状态的并存储会话信息,负载均衡器知道使用的cookie,并将流量从同一客户端引导到同一服务器,因此只有一台服务器需要保留会话信息。这使得服务器实现相对简单,但在操作中存在某些缺点:
- 负载均衡器需要能够处理粘性会话
- 客户端存在时,服务器需要保持联机,否则会话信息将被丢弃
- 如果客户端在地理位置上移动到另一个负载均衡器,它们可能会断开与会话的连接
-
使用共享会话存储后端,因此每个服务器基本上共享相同的信息。这消除了使用粘性会话的缺点,但显然重新引入了单个共享资源的瓶颈,并影响了可扩展性。使用良好的缓存策略可以在一定程度上缓解这种情况,但写入共享存储仍然需要巨大的后端。
-
保持一切无状态,并在客户端上尽可能多地处理。客户会记住自己的历史和/或购物篮内容。服务器所需要做的就是提供产品信息,这些信息不是特定于客户端的,因此具有极高的可扩展性。当然,当需要结账或做其他特定于客户端的事情时,服务器将需要执行特定于客户的操作,并使用某种或另一种会话,但与随意浏览相比,这只是流量的一小部分,问题要小得多。
在这种情况下,JWT用于携带需要验证的信息,如用户是谁,即身份验证。出于身份验证目的,您可以:
- 让客户端对每个请求进行身份验证,即在每个请求中发送用户名和密码。这显然是个坏主意,因为你不想不断地来回发送密码。它还需要每次都对后端的中央数据库进行查询,这再次破坏了可扩展性
- 为用户提供某种类型的令牌,以对其进行身份验证。这里的缺点是需要一个共享的令牌存储,请参阅上面的#2
JWT让您双管齐下:用户基本上在每次请求时都声称自己是用户X(无需发送密码),而且由于JWT由服务器签名,因此它允许服务器真正信任该声明。每个服务器都可以独立地验证签名,因此在每个请求上独立地信任声明,因此保持无状态,也不需要任何类型的共享存储。在JWT中存储信息的一个缺点是,它们只存在于一个客户端上,因此,一旦用户转移到另一个客户端或清除cookie,您在那里存储的任何信息都将不复存在;因此,仅使用JWT是不可能在用户帐户的设备之间同步购物篮的。
在实践中,您可能会使用至少两种方法,可能同时使用所有三种方法。您将有一些共享存储空间来存储帐户信息(包括购物篮),但您可以通过在粘性会话和/或JWT中缓存数据来尽可能减少与该存储空间的联系。通过JWT进行无状态身份验证是一件很容易的事情。对于其他一切,您可以在向共享存储施加负载、任何共享/缓存状态的最新程度和最终用户体验之间做出正确的权衡。