关于休息:会话真的违反了RESTfulness吗?

Do sessions really violate RESTfulness?

在RESTful API中使用会话是否真的违反了R??ESTfulness?我看到很多意见朝着两个方向发展,但我不相信会话是无REST的。在我看来,我的观点是:

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

会话如何违反这一点?

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

因此,对于客户端,会话cookie与任何其他基于HTTP头的身份验证机制完全相同,只是它使用Cookie头而不是Authorization或其他专有头。如果cookie值服务器端没有附加会话,为什么会产生影响呢?只要服务器表现为RESTful,服务器端实现就不需要关注客户端。因此,cookie本身不应该使API无REST,而会话只是客户端的cookie。

我的假设是错的吗?什么使会话cookie RESTless?


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

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

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

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

我希望所有这一切都有道理。您还应该查看维基百科关于具象状态转移的文章中的约束条款(如果您还没有)。关于REST的原则实际上在争论什么以及为什么,这一点尤其具有启发性。


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

  • REST风格:

    One can characterise applications conforming to the REST constraints
    described in this section as"RESTful".[15] If a service violates any
    of the required constraints, it cannot be considered RESTful.

    根据维基百科。

  • 无国籍约束:

    We next add a constraint to the client-server interaction:
    communication must be stateless in nature, as in the
    client-stateless-server (CSS) style of Section 3.4.3 (Figure 5-3),
    such that each request from client to server must contain all of the
    information necessary to understand the request, and cannot take
    advantage of any stored context on the server. Session state is
    therefore kept entirely on the client.

    根据菲尔丁的论文。

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

As such, to the client, a session cookie is exactly the same as any
other HTTP header based authentication mechanism, except that it uses
the Cookie header instead of the Authorization or some other
proprietary header.

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

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

From my point of view:

  • authentication is not prohibited for RESTfulness (otherwise there'd be little use in RESTful services)
  • authentication is done by sending an authentication token in the request, usually the header
  • this authentication token needs to be obtained somehow and may be revoked, in which case it needs to be renewed
  • the authentication token needs to be validated by the server (otherwise it wouldn't be authentication)

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

Figure 1. - Stateless authentication by trusted clients

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

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

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

Figure 2. - Stateless authentication by 3rd party clients

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

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


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

另一点是,要获得授权cookie,您可能希望先在某处提供您的凭据?如果是这样,那么它不会是RESTless吗?简单的例子:

  • 你尝试没有cookie的GET /a
  • 你以某种方式获得授权请求
  • 你会以某种方式授权POST /auth
  • 你得到Set-Cookie
  • 你尝试使用cookie GET /a。但GET /a在这种情况下是否表现得有意义?

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


实际上,RESTfulness仅适用于资源,如通用资源标识符所示。所以甚至谈论关于REST的标题,cookie等内容并不合适。 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状态。每个州都可以完全
通过它包含的表示和一组表达来理解
它提供的过渡,过渡限于a
统一的行动是可以理解的。系统可能是
一个复杂的状态图,但每个用户代理只能看到
一次一个状态(当前稳态),因此每个状态
状态简单,可以独立分析。用户,OTOH,
能够随时创建自己的过渡(例如,输入
URL,选择书签,打开编辑器等。)"

谈到身份验证问题,无论是通过cookie还是头文件完成,只要信息不是URI和POST有效负载的一部分,它根本就与REST无关。因此,关于无国籍,我们只讨论应用程序数据。

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

因此,客户端会跟踪用户正在做什么,并且只向服务器发送逻辑上完整的状态转换。


HTTP事务,基本访问认证不适用于RBAC,因为基本访问认证每次都使用加密的用户名:密码来识别,而RBAC中需要的是用户想要用于特定呼叫的角色。
RBAC不会验证用户名的权限,而是验证角色的权限。

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

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

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

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

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

如果有人有另一个想法,我会很高兴听到它。


  • 会话不是RESTless
  • 你的意思是REST服务仅供http使用,还是我的错误?基于Cookie的会话必须仅用于自己的(!)基于http的服务! (使用cookie可能会出现问题,例如来自Mobile / Console / Desktop /等)。
  • 如果您为3d party开发人员提供RESTful服务,请不要使用基于cookie的会话,而是使用令牌来避免安全性问题。