如果REST应用程序应该是无状态的,那么如何管理会话呢?

我需要一些澄清。我一直在阅读关于REST和构建基于REST的应用程序的书籍。根据维基百科,REST本身被定义为具象状态转移。因此,我不理解所有这些每个人都在吐槽的无国籍gobbledeygook

从维基百科:

在任何特定的时间,客户端可以在之间转换 申请声明或“休息中”。处于休息状态的客户端可以 与用户交互,但不创建负载,也不消耗每个客户端 服务器集或网络上的存储

他们只是说不要使用会话/应用程序级数据存储吗?

我知道REST的一个目标是使URI访问一致且可用,例如,不是在帖子中隐藏分页请求,而是使请求的页码成为get URI的一部分。对我来说很有道理。但是说没有每个客户端数据(会话数据)应该存储在服务器端似乎有点过头了。

如果我有一个消息队列,而我的用户想要读取消息,但在读取消息时,又想在会话期间阻止某些发送者的消息通过,该怎么办?将其存储在服务器端的某个位置,并让服务器只发送未被用户阻止的消息(或消息ID),这难道不合理吗?

每次请求新的消息列表时,我真的必须发送整个消息发送者列表来阻止吗?与我相关的消息列表在一开始就不应该是公开可用的资源。

我只是想理解一下。有人来澄清一下。


更新:

我发现了一个堆栈溢出的问题,有一个答案,不太让我一直在那里: 如何在REST中管理状态 这说客户端状态是重要的应该都转移到每个请求....Ugg . .看起来开销很大…这样对吗??< / p >

325980 次浏览

最基本的解释是:

服务器上没有客户端会话状态。

无状态意味着服务器不存储服务器端关于客户端会话的任何状态。

客户端会话存储在客户端上。服务器是无状态的,意味着每个服务器可以在任何时间为任何客户端服务,没有会话关联粘性的会话。相关的会话信息存储在客户机上,并根据需要传递给服务器。

这并不妨碍与web服务器通信的其他服务维护有关业务对象(如购物车)的状态,而不是有关客户端当前应用程序/会话状态的状态。

客户的应用程序状态永远不应该存储在服务器上,而是从客户端传递到每个需要它的地方。

这就是休息中的状态传输的由来。您传递状态,而不是让服务器存储它。如果没有别的原因,只是因为数百万个会话就是数百万个会话。

会话管理的负载被分摊到所有客户机上,客户机存储它们的会话状态,服务器可以以无状态的方式为多个数量级或更多的客户机提供服务。

即使对于一个您认为只有将需要成千上万并发用户的服务,您仍然应该使您的服务无状态。几万还是几万,会有时间和空间成本。

无状态是HTTP协议和web一般设计的操作方式,是一个总体上更简单的实现,您有一个单一的代码路径,而不是一堆服务器端逻辑来维护一堆会话状态。

这里有一些非常基本的实现原则:

这些是原则而不是实现,你如何满足这些原则可能会有所不同。

总之,五大原则是:

  1. 给每件“东西”一个ID
  2. 把事物联系起来
  3. 使用标准方法
  4. 具有多个表示形式的资源
  5. 无状态通信

在REST 论文中没有任何关于身份验证或授权的内容。

因为验证RESTful请求与验证非RESTful请求并没有什么不同。身份验证与RESTful讨论无关。

太宽 for StackOverflow说明了如何为您的特定需求创建无状态应用程序。

实现与REST相关的身份验证和授权更是如此,太宽和各种实现方法在互联网上都有详细的解释。

关于此请求帮助/信息的评论将/应该被标记为 不再需要

他们只是说不要使用会话/应用程序级数据存储吗?

不。他们不是在用一种无关紧要的方式说。

他们说不要定义“会话”。不登录。不注销。为请求提供凭据。每个请求都是独立的。

您仍然有数据存储。您仍然拥有身份验证和授权。您只是不需要浪费时间建立会话和维护会话状态。

关键在于,每个请求(a)都是完全独立的,(b)可以简单地外包给一个巨大的并行服务器群,而不需要任何实际工作。Apache或Squid可以盲目且成功地传递RESTful请求。

如果我有一个消息队列,而我的用户想要读取消息,但在读取消息时,又想在会话期间阻止某些发送者的消息通过,该怎么办?

如果用户需要一个过滤器,那么只需在每个请求上提供过滤器。

难道……不合理吗?服务器是否只发送未被用户阻止的消息(或消息ID) ?

是的。在RESTful URI请求中提供筛选器。

每次请求新的消息列表时,我真的必须发送整个消息发送者列表来阻止吗?

是的。这个“要阻止的消息发送者列表”可以有多大?一个简短的PK列表?

GET请求可以非常大。如果有必要,您可以尝试POST请求,尽管它听起来像一种查询。

您完全正确,支持与服务器的完全无状态交互确实给客户机增加了额外的负担。但是,如果考虑扩展应用程序,客户机的计算能力与客户机的数量成正比。因此,扩展到大量的客户是更加可行的。

只要您让服务器负责管理与特定客户端交互相关的一些信息,这个负担就会迅速增长到消耗服务器。

这是一种权衡。

无状态意味着每个HTTP请求都在完全隔离的情况下发生。当客户端发出HTTP请求时,它包含了服务器完成该请求所需的所有信息。服务器从不依赖以前请求的信息。如果该信息很重要,客户端将不得不在后续请求中再次发送它。无国籍也带来了新的特性。在负载平衡的服务器上分布无状态应用程序更容易。无状态应用程序也很容易缓存。

实际上有两种状态。客户机上的应用程序状态和服务器上的资源状态。

web服务只需要在实际发出请求时关心应用程序的状态。其他时候,它甚至不知道你的存在。这意味着无论客户端何时发出请求,都必须包含服务器处理请求所需的所有应用程序状态。

每个客户机的资源状态都是相同的,它的适当位置在服务器上。当您将图片上传到服务器时,您创建了一个新资源:新图片有自己的URI,可以作为未来请求的目标。您可以通过HTTP协议获取、修改和删除该资源。

希望这有助于区分无国籍和各种状态的含义。

整个概念是不同的……如果您试图实现RESTFul协议,则不需要管理会话。在这种情况下,最好对每个请求都进行身份验证(然而在性能方面有额外的成本-哈希密码将是一个很好的例子。没什么大不了的…)如果使用会话,如何在多个服务器上分配负载?我敢打赌RESTFul协议是为了消除会话——你真的不需要它们……这就是为什么它被称为“无状态”。只有当你发出请求后在客户端不能存储Cookie以外的任何东西时才需要会话(以旧的,不支持Javascript/ html5的浏览器为例)。在“全功能”RESTFul客户端情况下,在应用程序仍然加载之前,将base64(login:password)存储在客户端(内存中)通常是安全的-应用程序用于访问唯一的主机,并且cookie不能被第三方脚本泄露…

我强烈建议禁用RESTFul服务的cookie认证…检查Basic/Digest Auth -这对于基于rest的服务应该足够了。

用户应用程序状态管理的历史视图

传统意义上的会话将用户的状态保存在服务器内部的应用程序中。这可能是流中的当前页面,也可能是先前已输入但尚未持久化到主数据库中的内容。

这种需求的原因是客户端缺乏有效维护状态的标准,而不需要特定于客户端(即特定于浏览器)的应用程序或插件。

随着时间的推移,HTML5和XML Header Request已经标准化了在客户端(即浏览器端)以标准方式存储复杂数据(包括应用程序状态)的概念,而无需在服务器端来回传输。

REST服务的一般使用

REST服务通常在需要执行事务或需要检索数据时调用。

REST服务应该由客户端应用程序调用,而不是由最终用户直接调用。

进行身份验证

对于任何对服务器的请求,请求的一部分应该包含授权令牌。它是如何实现的取决于应用程序,但通常是BASICCERTIFICATE形式的身份验证。

REST服务不使用基于表单的身份验证。但是,如上所述,REST服务不是由用户调用,而是由应用程序调用。应用程序需要管理身份验证令牌的获取。在我的例子中,我使用了带有JASPIC与OAuth 2.0连接到谷歌进行身份验证的cookie和简单的HTTP身份验证来进行自动化测试。我也使用通过JASPIC进行HTTP头认证进行本地测试(尽管同样的方法可以在SiteMinder中执行)

根据这些示例,身份验证是在客户端进行管理的(尽管SiteMinder或谷歌将在其端存储身份验证会话),对于该状态无法做任何事情,但它不是REST服务应用程序的一部分。

检索请求

REST中的检索请求是GET操作,其中请求特定的资源并且是可缓存的。不需要服务器会话,因为请求拥有检索数据所需的一切:身份验证和URI。

事务脚本

如上所述,客户端应用程序本身调用REST服务以及它在客户端管理的身份验证。

这对于REST服务(如果操作正确的话)意味着将单个请求发送到REST服务器,该服务器将包含单个用户操作所需的所有内容,该操作执行单个事务所需的所有内容,该模式称为事务脚本

这通常通过POST请求完成,但也可以使用PUT等其他请求。

很多人为的REST示例(我自己做过)试图尽可能多地遵循HTTP协议中定义的内容,在经历了这些之后,我决定更加务实,并将其留给仅限GET和POSTPOST方法甚至不需要实现POST-REDIRECT-GET模式。

尽管如此,正如我上面提到的,客户端应用程序将是调用服务的应用程序,它只在需要时(不是每次)调用带有所有数据的POST请求。这可以防止对服务器的不断请求。

轮询

尽管REST也可以用于轮询,但我不推荐使用它,除非出于浏览器兼容性的考虑必须使用它。为此,我将使用WebSockets,我已经为它设计了API的合同。旧浏览器的另一个替代方案是CometD。

无状态意味着服务的状态在后续请求和响应之间不会持续。每个请求都携带自己的用户凭证,并单独进行身份验证。但是在有状态的情况下,每个请求都是从任何先前的请求中得知的。所有有状态的请求都是面向会话的,即每个请求都需要知道并保留之前请求中所做的更改。

银行应用程序是有状态应用程序的一个例子。用户首先登录,然后进行交易和注销。如果登出后用户试图进行交易,他将不能这样做。

是的,http协议本质上是一个无状态协议,但是为了使它有状态,我们创建了http cookie。因此,默认情况下是SOAP。但它也可以是有状态的,这取决于你使用的框架。

HTTP是无状态的,但是我们仍然可以通过使用不同的会话跟踪机制来维护java应用程序中的会话。

是的,我们也可以在webservice中维护会话,无论是REST还是SOAP。它可以通过使用任何第三方库实现,也可以由我们自己实现。

取自http://gopaldas.org/webservices/soap/webservice-is-stateful-or-stateless-rest-soap

您必须在客户端管理客户端会话。这意味着您必须在每个请求中发送身份验证数据,并且您可能(但不一定)在服务器上有一个内存缓存,它将身份验证数据与用户信息(如身份、权限等)配对。

这个REST 无国籍限制非常重要。如果不应用这个约束,您的服务器端应用程序将不会很好地规模,因为维护每个客户机会话将是它的阿基里斯之踵

无状态和有状态的主要区别在于每次都将数据传递回服务器。在无状态的情况下,客户端必须提供所有的信息,所以很多参数可能需要在每个请求中传递。在有状态的情况下,cliet只传递一次这些参数,它们由服务器维护,直到客户端再次修改。

在我看来,API应该是无状态的,这允许快速扩展。

我发现这里的基本问题是混淆了会话状态。虽然REST指定不应该在服务器上存储状态,但没有什么可以阻止您存储用户会话

管理服务器上的状态意味着服务器确切地知道客户机在做什么(它们在应用程序的哪个部分中查看哪个页面)。这是你不需要做的。

我同意其他人所说的,你应该保持会话存储的最小大小;虽然这是常识,但实际上也取决于应用程序。 因此,简而言之,您仍然可以保留一个带有缓存数据的会话,以较小的服务器负载处理请求,并通过提供一个临时身份验证/访问令牌供客户机使用来管理身份验证。当会话/令牌过期时,生成一个新的会话/令牌,并要求客户端使用它

有人可能会说,客户端应该更好地生成令牌。我说它是双向工作的,这取决于应用程序,以及谁将使用API。

在服务器上保留一些敏感的会话数据也是正确的做法。例如,您不能相信客户端会保留包含名为“isFreeGift”字段的购物车。这些信息应该保存在服务器上。

Santanu戴伊在他的回答中提供的视频链接很有帮助。如果你还没看过,就看看吧。

只是一个旁注:似乎所有已经给出的答案都忽略了一个事实,即某些操作可能会导致服务器负载过重。这与功耗、硬件消耗和成本(对于按CPU周期租用的服务器)有关。一个优秀的开发人员不应该懒惰地优化他们的应用程序,即使操作可以在一些租用的服务器上的现代CPU上快速完成,而且他们不需要支付电费和维护费用。

虽然这个问题是几年前的事了,但我希望我的回答仍然会有帮助。

REST非常抽象。有一些好的、简单的、真实的例子是有帮助的。

以所有主要的社交媒体应用程序为例——Tumblr、Instagram、Facebook和Twitter。它们都有一个永远滚动的视图,你往下滚动得越远,你看到的内容就越多,时间也越来越久远。然而,我们都经历过这样的时刻,你失去了你滚动到的位置,应用程序将你重置回顶部。比如,如果你退出了应用程序,那么当你重新打开它时,你又回到了顶部。

原因是服务器没有存储您的会话状态。遗憾的是,您的滚动位置只是存储在客户端的RAM中。

幸运的是,重新连接时不必重新登录,但这只是因为客户端存储的登录证书还没有过期。删除并重新安装应用程序,你将不得不重新登录,因为服务器没有将你的IP地址与你的会话关联。

服务器上没有登录会话,因为它们遵循REST。


现在,上面的例子根本不涉及web浏览器,但在后端,应用程序通过HTTPS与它们的主机服务器通信。我的观点是REST不必涉及cookie和浏览器等。存储客户端会话状态的方法多种多样。

但是让我们来讨论一下web浏览器,因为它带来了REST的另一个主要优势,这里没有人谈论这个优势。

如果服务器试图存储会话状态,它应该如何识别每个单独的客户机?

它不能使用他们的IP地址,因为许多人可以在共享路由器上使用相同的地址。那怎么做呢?

它不能使用MAC地址的原因有很多,最重要的是你可以在不同的浏览器和应用程序上同时登录多个不同的Facebook账户。一个浏览器可以很容易地伪装成另一个浏览器,MAC地址也很容易被欺骗。

如果服务器必须存储一些客户端状态来识别您,那么它必须将其存储在RAM中的时间比处理您的请求所需的时间长,否则它必须缓存该数据。服务器的RAM和缓存数量有限,更不用说处理器速度了。服务器端状态以指数方式增加了这三种状态。另外,如果服务器要存储关于会话的任何状态,那么它必须为你当前登录的每个浏览器和应用程序以及你使用的每个不同设备分别存储这些状态。


< p >所以…我希望您现在明白为什么REST对于可伸缩性如此重要。 我希望您可以开始理解为什么服务器端会话状态之于服务器可伸缩性,就像焊接铁砧之于汽车加速。< / p >

人们感到困惑的地方是认为“状态”指的是存储在数据库中的信息。不,它指的是当您使用服务器RAM时需要存储在它中的任何信息。

REST是无状态的,并且在请求之间不维护任何状态。客户端cookie /报头被设置为维护用户状态(如身份验证)。假设客户端用户名/密码由第三方认证机制验证——第二级OTP生成等。一旦用户获得身份验证,headers/cookies就会出现在服务端点,我们可以假设用户是身份验证者,因为用户带有有效的headers/cookies。现在用户的某些信息,如IP,要么保持在缓存中,如果请求来自相同的IP (mac地址)列出的资源,用户是允许的。并且缓存会维持一段特定的时间,一旦时间失效,缓存就会失效。因此,可以使用缓存或DB条目来保存请求的信息。

没有勺子。

不要认为无状态是“一次又一次地向服务器发送所有你的东西”。不可能。总会有状态——数据库本身一种状态毕竟,你是一个注册用户,所以任何一组客户端信息在没有服务器端的情况下都是无效的。严格来说,你从来都不是真正的无状态的。

关于登录的争论
    不保留会话和每次都登录吗?意味着什么

    有些人的意思是“每次都发送密码”,这简直太愚蠢了。有些人说“不,当然不是,还是发个令牌吧”;-你瞧,PHP会话就是这样做的。它发送一个会话id,这是一种令牌,它可以帮助你达到你的个人东西,而不是每次重新发送u/pw。它也非常可靠,并且经过了良好的测试。是的,方便也会变成缺点,见下一段。

减少碳足迹
    相反,你应该做的,真正有意义的是将你的web服务器占用最小化。像PHP这样的语言可以很容易地将所有东西都塞进会话存储中——但是会话是有价格标签的。如果你有几个web服务器,他们需要共享会话信息,因为他们也共享负载-他们中的任何一个都可能要为下一个请求服务

    必须使用共享存储。服务器至少需要知道某人是否登录。(如果你每次需要决定这个问题时都要麻烦数据库,那你就完蛋了。)共享存储需要比数据库快得多。这就带来了诱惑:好的,我有一个非常快的存储空间,为什么不把所有的东西都放在那里呢?——这就是事情以另一种方式变得糟糕的地方。

你的意思是,保持会话存储最小?
    再说一次,这是你的决定。你可以出于性能原因在那里存储东西(数据库几乎总是比Redis慢),你可以冗余存储信息,实现你自己的缓存,无论如何——只要记住,如果你在web服务器上存储了很多垃圾,它们会有更大的负载。此外,如果它们在重载下坏了(它们会坏的),你就会失去有价值的信息;使用REST思维方式,在这种情况下所发生的一切就是客户端再次发送相同的(!)请求,这次它得到了服务。
那该怎么做呢?
    这里没有万能的解决方案。我建议你选择一定程度的无国籍生活。Sessions可能被一些人喜欢,也可能被另一些人讨厌,但他们不会去任何地方。对于每一个请求,发送尽可能多的有意义的信息,也许再多一点;但是不要将无状态解释为没有会话,也不要将无状态解释为每次都登录。# EYZ0;PHP会话id是一个好方法,手动生成令牌是另一个好方法 思考和决定——不要让设计潮流替你思考。

这里的无状态意味着请求的状态或元数据不在服务器端维护。通过在服务器上维护每个请求或用户的状态,将导致性能瓶颈。服务器只是被请求提供执行任何特定操作所需的属性。

对于会话管理,或者为用户提供自定义体验,需要维护一些元数据或用户状态,可能是用户的偏好,过去的请求历史。这可以通过维护cookie、隐藏属性或进入会话对象来完成。

这可以维护或跟踪用户在应用程序中的状态。

希望这能有所帮助!

在开发RESTful服务时,为了登录,您需要对用户进行身份验证。一种可能的选择是在每次执行用户操作时发送用户名和密码。在这种情况下,服务器将根本不存储会话数据。

另一种选择是在服务器上生成一个会话id并将其发送给客户端,这样客户端就能够将会话id发送给服务器并使用该会话id进行身份验证。这比每次发送用户名和密码要安全得多,因为如果有人获得了这些数据,那么他/她就可以冒充用户,直到用户名和密码被更改为止。您可能会说,甚至会话id都可能被窃取,在这种情况下,用户将被模仿,您是对的。但是,在这种情况下,只有在会话id有效时才能模拟用户。

如果RESTful API需要用户名和密码才能更改用户名和密码,那么即使有人使用会话id冒充用户,黑客也无法锁定真正的用户。

会话id可以通过单向锁定(加密)某个标识用户的东西并将时间添加到会话id中来生成,这样就可以定义会话的过期时间。

服务器可能存储会话id,也可能不存储会话id。当然,如果服务器存储会话id,那么它将违反问题中定义的标准。但是,重要的是确保可以为给定用户验证会话id,这并不需要存储会话id。想象一下,如果您对电子邮件、用户id和一些特定于用户的私有数据(如最喜欢的颜色)进行单向加密,这将是第一级,并以某种方式将用户名日期添加到加密字符串中,并应用双向加密。因此,当接收到会话id时,可以对第二级进行解密,以确定用户声称是哪个用户名以及会话时间是否正确。如果这是有效的,那么可以通过再次执行加密并检查它是否匹配字符串来验证第一级加密。为此,您不需要存储会话数据。