会话真的违反了 RESTfulness 吗?

Do sessions really violate RESTfulness?

提问人:deceze 提问时间:5/20/2011 更新时间:12/12/2021 访问量:166194

问:

在 RESTful API 中使用会话真的违反了 RESTfulness 吗?我看到很多意见都朝着这两个方向发展,但我不相信会议是无休止的。从我的角度来看:

  • RESTfulness 不禁止身份验证(否则在 RESTful 服务中几乎没有用处)
  • 身份验证是通过在请求中发送身份验证令牌(通常是标头)来完成的
  • 此身份验证令牌需要以某种方式获取,并且可能会被吊销,在这种情况下,需要续订
  • 身份验证令牌需要由服务器验证(否则不会是身份验证)

那么会话是如何违反这一点的呢?

  • 客户端,会话是使用 cookie 实现的
  • Cookie 只是一个额外的 HTTP 标头
  • 可以随时获取和撤销会话 cookie
  • 如果需要,会话 cookie 可以具有无限的生命周期
  • 会话 ID(身份验证令牌)在服务器端进行验证

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

我的假设错了吗?是什么让会话 Cookie 变得无 RESTless

会话 REST Cookie RESTful 身份验证

评论

5赞 Will Hartung 5/20/2011
我在这里已经解决了这个问题:stackoverflow.com/questions/1296421/rest-complex-applications/......
5赞 Will Hartung 5/20/2011
除此之外,如果您仅使用会话进行身份验证,那么为什么不使用提供的标头呢?如果没有,并且您正在将会话用于对话的其他状态,则违反了 REST 的无状态约束。
3赞 deceze 5/20/2011
@Will谢谢。似乎您正在谈论用于临时存储用户提交数据的会话,而就我而言,我只是将它们作为身份验证的实现细节来谈论。这可能就是分歧的来源吗?
3赞 Will Hartung 5/20/2011
@deceze 我唯一的一点是,如果您要使用标头来表示身份验证令牌,HTTP 会提供通用 cookie 之外的令牌。所以,为什么不使用它并保留你从中获得的自由语义(任何看到有效负载的人都可以看到有一个分配给它的身份验证令牌)。
8赞 Will Hartung 5/20/2011
当然可以,但为什么不编造自己的标头,或者劫持其他一些身份验证令牌的标头。使用 X-XYZZY 标头。这只是语法,对吧?标头传达信息。Authorization 标头比 Cookie 更“自记录”,因为“每个人都”知道 Auth 标头的用途。如果他们只看到 JSESSIONID(或其他什么),他们就无法做出任何假设,或者更糟的是,做出错误的假设(他在会话中存储了什么,这还有什么用途,等等)。您是否在代码中将变量命名为 Aq12hsg?不,当然不是。同样的事情也适用于这里。

答:

367赞 Jared Harding 5/20/2011 #1

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

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

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

归根结底,您需要确保根据某种后备存储(数据库、缓存等)验证身份验证令牌,以确保保留尽可能多的 REST 属性。

我希望所有这些都是有道理的。如果您还没有的话,您还应该查看维基百科文章中关于具象状态转移的约束部分。关于REST的原则实际上在争论什么以及为什么,它特别有启发性。

评论

7赞 Darrel Miller 5/20/2011
我会改写你最初的发言。仅当 REST 的约束对应用程序有意义时,才使用 REST。您可以自由地应用这些约束的子集,您将获得一部分好处。但是,在这一点上,您已经创建了自己的架构风格。不过,这并不是一件坏事,事实上,这就是罗伊论文前四章的内容,原则性设计。REST只是一个例子。
1赞 Darrel Miller 5/20/2011
@Jared 您确定 Google 身份验证令牌中没有编码到期日期吗?似乎不是很难做到的。
4赞 Jared Harding 5/21/2011
@Darrel 一个足够公平的观点。老实说,我不确定谷歌是如何做到的,但过期时间可以编码到身份验证令牌中。不过,我相信我更大的观点仍然成立。有些类型的状态是必须维护的,只要你理解为什么REST要求无状态,你就可以以一种有意义的方式违反它,而不会对系统的其余部分产生许多影响,也不会对RESTful架构的优势产生许多影响。
13赞 deceze 5/24/2011
由于到目前为止还没有提出其他论点,我接受这个写得很好的回复。我认为重要的部分是无状态服务器并不意味着无状态服务器,我认为这经常被误解或误用。服务器可以(并且通常必须)具有它想要的任何状态,只要它的行为是幂等的
11赞 Ben Thurley 1/21/2013
我听过太多的讲道,以至于会议并不平静。但是,如果您尝试构建 Web 应用程序,HTTP 基本身份验证是一个真正的倒退。
-3赞 Maxim 5/20/2011 #2
  1. 会话不是无 RESTless 的
  2. 你的意思是REST服务仅供http使用还是我弄错了?基于 Cookie 的会话只能用于 own(!) http-based services!(使用cookie可能是一个问题,例如来自移动/控制台/桌面/等)
  3. 如果您为 3D 方开发人员提供 RESTful 服务,请不要使用基于 cookie 的会话,而是使用令牌以避免安全问题。

评论

4赞 roberkules 1/25/2012
Cookie 不应用于在持有身份验证令牌的服务器上存储会话的会话密钥。但是,如果 cookie 本身包含身份验证令牌,则这是一个可行的解决方案。(当然,cookie应该是httponly的,并且是安全的)
13赞 starteleport 1/24/2013 #3

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

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

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

总而言之,我认为,如果我们访问某些资源并且需要进行身份验证,那么我们必须在同一资源上进行身份验证,而不是在其他任何地方进行身份验证。

评论

1赞 deceze 1/24/2013
与此同时,我也更多地谈到了这个观点。我确实认为从技术上讲它没有什么区别,它只是 HTTP 标头。但是,如果需要通过单独的地址登录,则身份验证行为本身不是RESTful。因此,cookie只是身份验证系统存在更大问题的症状。
0赞 Oliver Charlesworth 8/14/2017
这并不能真正解释 Web 浏览器仅支持 或 .如果你想在浏览器上下文中做任何比基本身份验证或摘要式身份验证更高级的事情(你应该这样做),那么你将需要除标头之外的东西。Authorization: BasicDigestAuthorization
1赞 Oliver Charlesworth 8/16/2017
绝对 - 如果你在做纯 JS,那么事情基本上没问题(例如,Websockets 除外)。但我的观点是,在浏览器场景中,基于 API 的身份验证不一定是唯一的考虑因素。
8赞 TRiG 8/1/2019
GET /a没有 cookie 和有 cookie 显然是两个不同的请求,它们的行为不同是可以接受的。
1赞 Jasper 2/13/2020
@TRiG此外,按照此逻辑,使用身份验证标头也与不使用身份验证标头相同,因此同样不适用于 REST。如果你打算将一个 http 标头与另一个 http 标头区别对待,你至少要解决这个问题。GET /aGET /a
338赞 inf3rno 12/1/2013 #4

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

  • 宁静:

    可以描述符合REST约束的应用程序 在本节中描述为“RESTful”。[15] 如果某项服务违反了任何 在所需的约束中,它不能被视为 RESTful。

    根据维基百科

  • 无状态约束:

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

    根据菲尔丁的论文

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

因此,对于客户端来说,会话 cookie 与任何会话 cookie 完全相同 其他基于 HTTP 标头的身份验证机制,但其使用 Cookie 标头而不是 Authorization 或其他一些 专有标头。

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

在我看来,饼干没有错。Cookie 技术是一种客户端存储机制,其中存储的数据会通过每个请求自动附加到 Cookie 标头。我不知道REST约束在这种技术上有问题。所以技术本身没有问题,问题在于它的使用。菲尔丁写了一个小节,说明为什么他认为HTTP cookie是坏的。

从我的角度来看:

  • RESTfulness 不禁止身份验证(否则在 RESTful 服务中几乎没有用处)
  • 身份验证是通过在请求中发送身份验证令牌(通常是标头)来完成的
  • 此身份验证令牌需要以某种方式获取,并且可能会被吊销,在这种情况下,需要续订
  • 身份验证令牌需要由服务器验证(否则不会是身份验证)

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

Figure 1. - Stateless authentication by trusted clients

  • 图 1.- 受信任客户端的无状态身份验证

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

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

Figure 2. - Stateless authentication by 3rd party clients

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

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

评论

6赞 deceze 12/1/2013
是的,你是完全正确的。自从我发布了这个问题以来,我完全看到了这一点。从技术细节来看,会话 cookie 并没有什么特别之处,但这缺少树木的森林。因为漂亮的图表,所以接受了你的答案。;)
1赞 inf3rno 12/1/2013
好的,我重新考虑了一下,REST服务的响应不应该依赖于授权,所以我认为前2个解决方案是100%可以的,如果服务仅使用信息来决定它是否允许请求,那么其他解决方案就可以了。所以我认为用户权限应该对当前资源的表示产生影响。
3赞 jcoffland 2/4/2014
@inf3rno,完全 RESTful 服务确实不能像传统实现方式那样依赖会话 cookie 进行身份验证。但是,如果 cookie 包含服务器稍后需要的所有状态信息,则可以使用 cookie 来执行身份验证。您还可以通过使用公钥/私钥对 cookie 进行签名来确保 cookie 不被篡改。请参阅下面的评论。
2赞 inf3rno 6/8/2018
@SiminJie我认为你必须问问自己,你是否真的需要一个REST API。这通常不是合理的解决方案。我的意思是,大多数开发人员不理解或不关心许多限制。它需要大量的学习才能正确地做到这一点,而只有真正的大型服务才值得付出努力。做你已经了解的事情,并通过一个爱好项目来尝试,要容易得多。
16赞 lvoelk 9/19/2018
我不明白为什么每个人似乎都接受您应该在客户端存储密码并在每个请求时发送密码的评论。这是一种非常糟糕的做法,会危及客户的敏感数据。一个未经哈希处理的密码(必须一遍又一遍地发送)永远不应该存储在任何地方。如果我们接受这一点,那么您正在像大多数身份验证系统一样使用令牌,在这种情况下,无论我们用于扩展令牌存储库的机制,其可伸缩性问题与任何会话可伸缩性基本相同。
11赞 Ken Kopelson 12/13/2013 #5

实际上,RESTfulness 仅适用于 RESOURCES,如通用资源标识符所示。因此,甚至谈论诸如标头,cookie等与REST有关的东西都不太合适。REST可以在任何协议上工作,即使它恰好是通过HTTP完成的。

主要的决定因素是:如果您发送一个 REST 调用,这是一个 URI,那么一旦调用成功发送到服务器,该 URI 是否返回相同的内容,假设没有执行任何转换(PUT、POST、DELETE)?此测试将排除返回的错误或身份验证请求,因为在这种情况下,请求尚未到达服务器,这意味着将返回与给定 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还是header完成,只要信息不是URI和POST有效负载的一部分,它实际上与REST完全无关。因此,关于无状态,我们只谈论应用程序数据。

例如,当用户在 GUI 屏幕中输入数据时,客户端会跟踪哪些字段已输入、哪些字段未输入、缺少任何必填字段等。这都是 CLIENT CONTEXT,不应由服务器发送或跟踪。发送到服务器的是需要在 IDENTIFIED 资源中(通过 URI)修改的完整字段集,以便该资源从一个 RESTful 状态转换到另一个 RESTful 状态。

因此,客户端会跟踪用户正在执行的操作,并且只向服务器发送逻辑上完成的状态转换。

评论

3赞 jcoffland 2/4/2014
我看不出这如何阐明所提出的问题。
1赞 Bert Verhees 6/16/2015 #6

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

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

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

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

这就是我喜欢REST不应该被视为一种宗教的答案的原因。例如,在复杂的商业案例中,在医疗保健领域,RBAC 是绝对常见和必要的。如果他们不被允许使用REST,那将是一个遗憾,因为所有的REST工具设计者都会将REST视为一种宗教。

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

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

0赞 Mohammad Tofic Mohammad 5/17/2020 #7

我认为令牌必须包含其中编码的所有所需信息,这使得通过验证令牌和解码信息进行身份验证 https://www.oauth.com/oauth2-servers/access-tokens/self-encoded-access-tokens/

-2赞 user14699123 12/15/2020 #8

不,使用会话并不一定违反 RESTfulness。如果您遵守 REST 规则和约束,那么使用会话(维护状态)将是多余的。毕竟,RESTfulness 要求服务器不维护状态。

评论

2赞 user14699123 12/15/2020
在我看来,大多数响应都误解了 API 是 RESTful 意味着什么。RESTful API 满足 REST 约束:统一接口、无状态、可缓存、客户端-服务器、分层系统、按需代码。您的 API 很有可能在满足这些约束的同时实现会话。
2赞 Ahmet Firat Keler 12/12/2021 #9

据我了解,当我们谈论会话时,有两种类型的状态

  • 客户端和服务器交互状态
  • 资源状态

这里的无状态约束是指 Rest 中的第二种类型。 使用 cookie(或本地存储)不会违反 Rest,因为它与第一种类型相关。

Fielding说:“从客户端到服务器的每个请求都必须包含理解请求所需的所有信息,并且不能利用服务器上存储的任何上下文。因此,会话状态完全保留在客户端上。

这里的问题是,在服务器上要满足的每个请求都需要来自客户端的所有必要数据。然后,这被认为是无状态的。再说一遍,我们在这里谈论的不是 cookie,而是资源。