为啥将用户标识符存储在 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 中却不是?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 Firefox 并不总是为 POST 请求发送 HTTP Origin 标头?
如果我在使用 cors 时添加标准 http 标头,为啥会预检请求?
浏览器在 ajax 请求上发送 Origin 标头,但不是通过简单的 HTTP Get 请求。为啥?