为啥将用户标识符存储在 HTTP 请求标头字段中被认为是无状态的,但将其存储在 URI 中却不是?

Posted

技术标签:

【中文标题】为啥将用户标识符存储在 HTTP 请求标头字段中被认为是无状态的,但将其存储在 URI 中却不是?【英文标题】:Why is storing a user identifier in an HTTP request header field considered stateless but storing it in a URI is not?为什么将用户标识符存储在 HTTP 请求标头字段中被认为是无状态的,但将其存储在 URI 中却不是? 【发布时间】:2021-08-14 01:25:03 【问题描述】:

用户标识符绝不应存储在 URI 中。这是为了根据 Roy Fielding,§ 6.2.5 ‘REST Mismatches in URI’ 他的博士论文 Architectural Styles and the Design of Network-Based Software Architectures 实施 REST 无状态约束(无服务器端应用程序状态):

虽然 URI 设计符合 REST 的架构概念 标识符,单独的语法不足以强制命名权限 根据资源模型定义自己的URI。 一种形式 滥用是包括识别当前用户的信息 在超媒体响应引用的所有 URI 中 表示。 此类嵌入的用户 ID 可用于维护会话 服务器上的状态,通过记录用户的操作来跟踪用户行为,或者 在多个动作中携带用户偏好(例如,Hyper-G 的 网关[84])。但是,通过违反 REST 的约束,这些 系统也会导致共享缓存失效,减少服务器 可扩展性,并在用户共享时导致不良影响 那些与其他人的参考。

根据 Roy Fielding,§ 5.1.2 ‘Considerations for New Authentication Schemes’ of his RFC 7235 的说法,HTTP/1.1 基本身份验证遵守 REST 无状态约束:

假定 HTTP 身份验证是无状态的:所有 必须提供验证请求所需的信息 请求,而不是依赖于服务器记住之前 请求。 基于或绑定到底层的身份验证 连接超出了本规范的范围,并且本质上 有缺陷,除非采取措施确保无法连接 由经过身份验证的用户以外的任何一方使用(参见第 2.3 节 [RFC7230])。

然而,根据 Roy Fielding,§ 2.1 ‘Challenge and Response’ of his RFC 7235 的说法,HTTP/1.1 允许在请求标头字段 Authorization 中存储用户标识符以向源服务器验证用户代理:

希望通过原始服务器验证自己的用户代理 -- 通常,但不一定,在收到 401(未经授权)之后 -- 可以通过在请求中包含 Authorization 标头字段来做到这一点。

那么为什么将用户标识符存储在 HTTP 请求标头字段中被认为是无状态的,但将其存储在 URI 中却不是?

在我看来,无论通道(URI、标头字段、正文)如何传输用户标识符,都可以将用户代理标识为源服务器,因此允许为该用户代理存储服务器端应用程序状态。换句话说,对我来说,身份验证实际上是实现有状态通信的手段。

【问题讨论】:

【参考方案1】:

查看菲尔丁对 5.1.3 (Stateless) 的描述可能会有所帮助:

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

这是“无状态”的关键思想 - 您应该能够独立于所有其他请求理解每个请求的语义。


那么为什么将用户标识符存储在 HTTP 请求标头字段中被认为是无状态的,但将其存储在 URI 中却不是?

不是。两种机制都是无状态的(正如 Fielding 在 REST 上下文中定义的那样)。

Fielding 在 6.2.5 中描述的违反不是违反 5.1.3,而是更好地理解为违反 5.1.4 (Cache) 和 5.1.5 ( Uniform Interface)。

换一种说法:在服务器上存储会话状态是不好的(它违反了 5.1.3,因此会影响您的一些扩展);无关紧要的是,您是使用 Cookie 标头中的信息、身份验证标头中的信息还是 URI 中的信息来访问缓存的会话状态。

但是将会话状态密钥编码到您的资源标识符中会影响您的缓存,以及您使用资源标识符作为资源表示的代理的能力。

【讨论】:

现在,如果 HTTP 身份验证不访问存储在服务器上的应用程序状态,那么它的意义何在,从而违反了无状态约束?我问这个是因为我一直认为用户特定状态 = 应用程序状态,共享状态 = 资源状态。 身份验证的重点是访问控制——它是一种用于确定请求权限的标准化机制。它不违反无状态约束,因为每个授权请求都带有自己的授权凭据(服务器不必“记住”任何先前请求的信息。 好的,换句话说,身份验证的重点是授权(访问资源)。所以你是说永远不要访问用户特定的数据,否则它违反了无状态约束。因此,所有存储用户特定数据(例如 GitHub、Stack Overflow)、支付信息或交付信息(例如 Amazon、Uber Eats)的网站都不是 RESTful? 不,这不是我所说的正确解释。拥有其表示包括用户信息的资源是非常好的。资源状态(表示随时间变化的隶属函数的当前表达式)和会话状态(客户端和服务器之间交互的上下文)是不同的东西。 “想一想,直到你这样做,这是一个重要的概念”。在网络环境中研究这个问题——浏览器知道什么与服务器知道什么?在 Fielding 论文中,复习 5.3.3、3.4.6、3.4.7。

以上是关于为啥将用户标识符存储在 HTTP 请求标头字段中被认为是无状态的,但将其存储在 URI 中却不是?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 Ajax 发布请求在表单提交中被阻止?

为啥 Firefox 并不总是为 POST 请求发送 HTTP Origin 标头?

如果我在使用 cors 时添加标准 http 标头,为啥会预检请求?

浏览器在 ajax 请求上发送 Origin 标头,但不是通过简单的 HTTP Get 请求。为啥?

net/http:请求已取消(等待标头时超出 Client.Timeout)为啥/如何处理?

CORS 标头在浏览器中被剪切,状态码 >= 400