会话真的违反了R​​ESTfulness吗?


490

在RESTful API中使用会话是否真的违反了R​​ESTfulness?我已经看到许多意见朝着任何方向发展,但是我不相信会议是RESTless的。从我的观点:

  • RESTfulness禁止身份验证(否则RESTful服务将很少使用)
  • 通过在请求中发送身份验证令牌(通常是标头)来完成身份验证
  • 该身份验证令牌需要以某种方式获得,并且可能会被吊销,在这种情况下,需要对其进行续订
  • 身份验证令牌需要由服务器验证(否则将不是身份验证)

那么会话如何违反此规定?

  • 客户端,使用cookie实现会话
  • Cookies只是一个额外的HTTP标头
  • 可以随时获取并撤消会话Cookie
  • 如果需要,会话cookie可以有无限的生存时间
  • 会话ID(身份验证令牌)在服务器端经过验证

因此,对于客户端而言,会话cookie与任何其他基于HTTP标头的身份验证机制完全相同,只是它使用Cookie标头代替Authorization或其他专有标头。如果没有会话附加到Cookie值服务器端,那为什么会有所作为?只要服务器表现为 RESTful ,服务器端的实现就不必关心客户端。因此,Cookie本身不应使API 成为RESTless,而会话只是客户端的Cookie。

我的假设错了吗?是什么使会话cookie 无需REST



5
除此之外,如果您仅使用会话进行身份验证,那么为什么不使用提供的标头呢?如果不是这样,并且您将会话用于会话的其他状态,那么这违反了REST的无状态约束。
哈顿

2
@会谢谢。看来您在谈论临时存储用户提交的数据的会话,而就我而言,我只是在谈论它们作为身份验证的实现细节。这可能是分歧的根源吗?
deceze

3
@deceze我唯一的要点是,如果您要使用头来表示身份验证令牌,则HTTP会提供一个通用Cookie之外的令牌。因此,为什么不使用它并保留它获得的自由语义(任何看到有效负载的人都可以看到分配了身份验证令牌)。
Will Hartung

7
可以,但是为什么不组成自己的标头,或者劫持auth令牌的其他标头。使用X-XYZZY标头。只是语法正确吗?标头传达信息。Authorization标头比您的cookie更具有“自我记录”功能,因为“每个人”都知道Auth标头的用途。如果他们仅看到JSESSIONID(或其他内容),则他们无法做出任何假设,或更糟糕的是,做出错误的假设(他在会话中存储了什么,这又有什么用,等等)。您是否在代码Aq12hsg中命名了变量?不,当然不。同样的事情在这里适用。
Will Hartung

Answers:


299

首先,让我们定义一些术语:

  • RESTful:

    可以将符合本节中描述的REST约束的应用程序描述为“ RESTful”。[15] 如果服务违反任何必需的约束,则不能将其视为RESTful。

    根据维基百科

  • 无状态约束:

    接下来,我们为客户端-服务器交互添加了一个约束:通信本质上必须是无状态的,如第3.4.3节(图5-3)的客户端-状态-服务器(CSS)样式所示,以便从客户端到客户端的每个请求服务器必须包含理解请求所需的所有信息,并且不能利用服务器上存储的任何上下文。因此,会话状态完全保留在客户端上。

    根据菲尔丁论文

因此,服务器端会话违反了REST的无状态约束,因此也违反了RESTfulness。

因此,对于客户端而言,会话cookie与任何其他基于HTTP标头的身份验证机制完全相同,只是它使用Cookie标头代替Authorization或其他专有标头。

通过会话cookie,您可以将客户端状态存储在服务器上,因此您的请求具有上下文。让我们尝试向系统中添加负载均衡器和另一个服务实例。在这种情况下,您必须在服务实例之间共享会话。这样的系统很难维护和扩展,因此扩展性很差。

我认为cookie没什么错。cookie技术是一种客户端存储机制,其中存储的数据会根据每个请求自动附加到cookie头。我不知道这种技术有问题的REST约束。因此,技术本身没有问题,问题在于其用途。菲尔丁写了一个小节,说明为什么他认为HTTP cookie不好。

从我的观点:

  • RESTfulness禁止身份验证(否则RESTful服务将很少使用)
  • 通过在请求中发送身份验证令牌(通常是标头)来完成身份验证
  • 该身份验证令牌需要以某种方式获得,并且可能会被吊销,在这种情况下,需要对其进行续订
  • 身份验证令牌需要由服务器验证(否则将不是身份验证)

您的观点非常扎实。唯一的问题是在服务器上创建身份验证令牌的概念。您不需要那部分。您需要在客户端上存储用户名和密码,并在每次请求时将其发送。除了HTTP基本身份验证和加密连接之外,您不需要执行其他操作:

图1.-可信客户端的无状态认证

  • 图1.-可信客户端的无状态认证

您可能需要在服务器端使用内存中的身份验证缓存来加快处理速度,因为您必须对每个请求进行身份验证。

现在,这对于您编写的受信任的客户来说效果很好,但是第三方客户呢?他们不能具有用户名和密码以及用户的所有权限。因此,您必须分别存储第三方客户端可以由特定用户拥有的权限。因此,客户端开发人员可以注册他们的第三方客户端,并获得唯一的API密钥,并且用户可以允许第三方客户端访问其部分权限。例如阅读名称和电子邮件地址,或列出他们的朋友等。。。允许第三方客户端后,服务器将生成访问令牌。这些访问令牌可由第三方客户端用来访问用户授予的权限,如下所示:

图2.-第三方客户端的无状态认证

  • 图2.-第三方客户端的无状态认证

因此,第三方客户端可以从受信任的客户端(或直接从用户)获取访问令牌。之后,它可以发送带有API密钥和访问令牌的有效请求。这是最基本的第三方身份验证机制。您可以在每个第三方认证系统的文档(例如OAuth)中阅读有关实施细节的更多信息。当然,这可能更加复杂和安全,例如,您可以在服务器端对每个请求的详细信息进行签名,并将签名与请求一起发送,依此类推……实际的解决方案取决于您应用程序的需求。


5
是的,您完全正确。自从我发布了这个问题以来,我已经完全明白了这一点。从技术细节上看,会话cookie并没有什么特别的,但是缺少了树林。由于图表精美,因此接受了您的回答。;)
deceze

1
好吧,我重新考虑一下,REST服务的响应不应该取决于授权,因此我认为前两种解决方案是100%可以的,而其他解决方案是可以的,如果该服务仅使用信息来决定是否允许请求或不。因此,我认为用户权限应影响当前资源的表示形式。
inf3rno 2013年

1
我将创建一个关于表示形式的权限依赖性的问题。一旦获得适当的解决方案,我将扩展这个答案。
inf3rno 2013年

3
@ inf3rno,确实,完全RESTful服务不能以传统上实现方式的方式依赖会话cookie进行身份验证。但是,如果Cookie包含服务器以后需要的所有状态信息,则可以使用Cookie进行身份验证。您还可以通过使用公钥/私钥对对其进行签名来防止cookie被篡改。请在下面查看我的评论。
jcoffland 2014年

3
我不明白为什么每个人似乎都接受您应该将密码存储在客户端并随请求一起发送的评论。这是非常糟糕的做法,并且会危害客户的敏感数据。未加密的密码(必须反复发送该密码)永远不应存储在任何地方。如果我们接受这一点,那么您将像大多数身份验证系统一样使用令牌,在这种情况下,我们用来扩展令牌存储库的任何机制都将具有与任何会话可伸缩性相同的可伸缩性。
lvoelk

334

首先,REST不是一种宗教,不应这样对待。尽管RESTful服务有很多优点,但您应该仅遵循REST的原则,只要它们对您的应用程序有意义。

也就是说,身份验证和客户端状态不违反REST原则。尽管REST要求状态转换必须是无状态的,但这是指服务器本身。本质上,所有REST都与文档有关。无状态背后的想法是SERVER是无状态的,而不是客户端。任何发出相同请求(相同的标头,Cookie,URI等)的客户端都应被带到应用程序中的同一位置。如果网站存储了用户的当前位置并通过更新此服务器端导航变量来管理导航,则将违反REST。根据服务器端的状态,具有相同请求信息的另一个客户端将被带到不同的位置。

Google的Web服务是RESTful系统的绝佳示例。它们要求在每次请求时都传递带有用户身份验证密钥的身份验证标头。这确实略有违反REST原则,因为服务器正在跟踪身份验证密钥的状态。必须保持该密钥的状态,并且该密钥具有某种到期日期/时间,之后它将不再授予访问权限。但是,正如我在文章顶部提到的那样,必须做出牺牲才能使应用程序真正起作用。也就是说,身份验证令牌的存储方式必须允许所有可能的客户端在其有效时间内继续授予访问权限。如果一台服务器将身份验证密钥的状态管理到另一台负载平衡服务器无法接管基于该密钥的执行请求的程度,您已经开始真正违反REST原则。Google的服务可确保您可以随时使用手机上针对负载均衡服务器A的身份验证令牌,并从台式机访问负载均衡服务器B,并且仍然可以访问系统,并且可以访问相同的资源要求是相同的。

总而言之,您需要确保针对某种类型的后备存储(数据库,缓存等)对身份验证令牌进行验证,以确保保留尽可能多的REST属性。

我希望所有这些都是有道理的。您也应该检查约束部分的的上表述性状态转移维基百科的文章,如果您还没有。关于REST的宗旨实际上是在争论什么以及为什么这样做,这尤其具有启发性。


6
我要重新表述您的最初声明。仅当REST的约束对您的应用程序有意义时才使用REST。您可以自由地应用这些约束的子集,并且可以获得部分好处。但是,此时您已经创建了自己的建筑风格。但这并不是一件坏事,实际上,这就是Roy论文的前四章所涉及的原则性设计。REST只是一个例子。
Darrel Miller

4
@Darrel足够公平的一点。老实说,我不确定Google是如何做到的,但是可以将到期时间编码到身份验证令牌中。我相信我的更大观点仍然存在。有些类型的状态仅需维护,只要您了解REST 为什么要求无状态,就可以以合理的方式违反状态,而不会对系统的其余部分产生很多影响,并且具有RESTful架构的优点。
贾里德·哈丁

7
由于到目前为止还没有其他论点,因此我接受书面答复。我认为重要的是无状态服务器并不意味着无状态服务器,我认为这常常被误解或误用。只要服务器表现为幂等,服务器就可以(通常必须)具有所需的任何状态。
deceze

10
我听过这么多的讲道,以至于会议不安宁。如果您尝试构建Web应用程序,则HTTP基本身份验证是真正的倒退。
Ben Thurley

1
@Micah Henning,您错误地假设服务器需要状态信息来验证身份验证令牌。如果您不知道私钥,我们可以合理地假设您不能伪造由公钥/私钥对签名的令牌。要验证令牌有效,您只需要公共密钥即可。我仍然认为完全可以实现RESTful身份验证。
jcoffland 2014年

12

Cookies不用于身份验证。为什么要重新发明轮子?HTTP具有精心设计的身份验证机制。如果使用Cookie,则只能使用HTTP作为传输协议,因此我们需要创建自己的信令系统,例如,告诉用户他们提供了错误的身份验证(使用HTTP 401可能是不正确的,因为我们可能不会这样做)Www-Authenticate根据HTTP规范要求提供给客户端:))。还应注意,这Set-Cookie只是对客户的建议。它的内容可以保存也可以不保存(例如,如果禁用了cookie),而Authorization标头则在每个请求时自动发送。

另一点是,要获取授权cookie,您可能需要首先在某个地方提供凭据?如果是这样,那不是RESTless吗?简单的例子:

  • 您尝试GET /a不使用Cookie
  • 您以某种方式收到授权请求
  • 您去以某种方式授权 POST /auth
  • 你得到 Set-Cookie
  • 您尝试GET /a 使用 cookie。但是GET /a在这种情况下行为是否幂等?

综上所述,我相信如果我们访问某些资源并且需要进行身份验证,那么我们必须在同一资源上而不是其他任何地方进行身份验证。


1
同时,我也更多地从这个角度出发。我确实认为从技术上讲它没什么区别,只是HTTP标头。如果需要通过单独的地址登录,则身份验证行为本身不是RESTful,这是事实。因此,cookie只是身份验证系统出现较大问题的征兆。
deceze

这实际上并不能说明网络浏览器仅支持Authorization: Basic或的事实Digest。如果您想在浏览器上下文中执行比基本身份验证或摘要身份验证更高级的操作(并且应该这样做),那么您将需要除Authorization标头之外的其他功能。
奥利弗查尔斯沃思

1
绝对-如果您使用的是纯JS,那么一切基本上都可以(例如Websockets除外)。但我的观点是,基于API的身份验证不一定是浏览器方案中的唯一考虑因素。
奥利弗·查尔斯沃思

5
GET /a没有Cookie和带有Cookie的请求显然是两个不同的请求,它们的行为也可以接受。
TRiG

1
要添加到@TRiG,请遵循以下逻辑,GET /a带有身份验证标头也与GET /a没有身份验证标头的情况相同,这使得它同样不可用于REST。如果您打算将一个HTTP标头与另一个HTTP标头区别对待,则至少要解决这个问题。
贾斯珀

7

实际上,如通用资源标识符所示,RESTfulness仅适用于资源。因此,就REST而言,甚至谈论诸如标头,Cookie等之类的东西也不是很合适。REST可以在任何协议上工作,即使它通常是通过HTTP常规完成的。

主要的确定因素是:如果您发送一个REST调用(即URI),则该调用成功将其发送到服务器后,假设未执行任何转换(PUT,POST,DELETE),该URI是否会返回相同的内容。 ?该测试将排除返回的错误或身份验证请求,因为在这种情况下,该请求尚未发送到服务器,这意味着将返回与给定URI相对应的文档的servlet或应用程序。

同样,对于POST或PUT,是否可以发送给定的URI /有效载荷,并且无论发送消息多少次,它始终会更新相同的数据,以便后续的GET返回一致的结果?

REST与应用程序数据有关,而不与传输数据所需的低级信息有关。

在下面的博客文章中,Roy Fielding对整个REST想法进行了很好的总结:

http://groups.yahoo.com/neo/groups/rest-discuss/conversations/topics/5841

“ RESTful系统从一个稳定状态发展到下一个稳定状态,每个这样的稳定​​状态既是潜在的开始状态,又是潜在的结束状态。也就是说,RESTful系统是数量不多的组件,它们遵循一组简单的状态。规则,使其始终处于REST状态或从一个RESTful状态转换为另一种RESTful状态每种状态都可以通过其包含的表示形式和提供的一组转换来完全理解,并且转换仅限于统一系统可能是一个复杂的状态图,但是每个用户代理一次只能看到一个状态(当前的稳态),因此每个状态都很简单,可以独立进行分析。用户OTOH能够随时创建自己的转换(例如,输入URL,选择书签,打开编辑器,等等。”


关于身份验证的问题,无论它是通过cookie还是标题完成的,只要该信息不是URI和POST有效负载的一部分,它实际上就与REST无关。因此,关于无状态,我们仅在谈论应用程序数据。

例如,当用户在GUI屏幕中输入数据时,客户端将跟踪已输入的字段,未输入的字段,缺少的所有必填字段等。这都是CLIENT CONTEXT,因此不应发送或跟踪由服务器。发送到服务器的是在IDENTIFIED资源中需要修改的完整字段集(通过URI),以使该资源中发生从一个RESTful状态到另一种RESTful状态的过渡。

因此,客户端跟踪用户的操作,并且仅将逻辑上完整的状态转换发送到服务器。


3
我看不出这对提出的问题有何启示。
jcoffland 2014年

1

HTTP事务(基本访问身份验证)不适用于RBAC,因为基本访问身份验证每次都使用加密的username:password进行标识,而RBAC中需要的是用户要用于特定呼叫的角色。RBAC不会验证用户名的权限,但会验证角色的权限。

您可以尝试像这样进行串联:usernameRole:password,但这是不好的做法,而且效率低下,因为当用户拥有更多角色时,身份验证引擎将需要测试串联中的所有角色,并且再次调用每个调用。这将破坏RBAC的最大技术优势之一,即非常快速的授权测试。

因此,使用基本访问身份验证无法解决该问题。

为了解决此问题,会话维护是必要的,并且根据一些答案,这似乎与REST相矛盾。

这就是我喜欢REST不应该被视为宗教的答案。例如,在复杂的业务案例中,在医疗保健中,RBAC是绝对常见和必要的。如果由于所有REST工具设计人员都将REST视为一种宗教而被禁止使用REST,那将是一个遗憾。

对我而言,通过HTTP维护会话的方法并不多。可以使用带有sessionId的cookie或带有sessionId的标头。

如果有人有其他想法,我会很高兴听到。



-4
  1. 会话并非非RESTless
  2. 您是说REST服务仅用于http-使用还是我弄错了?基于Cookie的会话只能用于基于自己的(!)http的服务!(使用Cookie可能是一个问题,例如从Mobile / Console / Desktop / etc中使用。)
  3. 如果您为3d party开发人员提供RESTful服务,请不要使用基于cookie的会话,而应使用令牌来避免安全性问题。

3
Cookie不应用于在拥有身份验证令牌的服务器上为会话存储会话密钥。但是如果Cookie本身拥有身份验证令牌,则这是可行的解决方案。(当然,Cookie应该是httponly并且是安全的)
roberkules 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.