【问题标题】:Correct http status code for resource which requires authorization更正需要授权的资源的 http 状态码
【发布时间】:2012-01-13 10:13:18
【问题描述】:

如果用户尝试访问需要用户登录的页面,返回的正确 http 状态代码似乎存在很多混淆。

那么当我显示登录页面时,基本上会发送什么状态码?

我很确定我们需要使用4xx 范围内的状态码。

我在这里不是在谈论 HTTP 身份验证,所以至少有 1 个状态代码我们不会使用 (401 Unauthorized)。

现在我们应该使用什么?答案(也在 SO 上)似乎有所不同:

根据答案here,我们应该使用403 Forbidden

但是在状态码的描述中是:

授权无济于事,不应重复请求。

嗯,这看起来不像是正确的。因为授权会有所帮助。

所以让我们看看其他答案。答案here 甚至根本不使用4xx 范围,而是使用302 Found

302 Found状态码的描述:

请求的资源暂时位于不同的 URI 下。由于重定向有时可能会改变,客户端应该继续使用 Request-URI 来处理未来的请求。此响应仅在 Cache-Control 或 Expires 标头字段指示时才可缓存。

我认为这也不是我想要的。因为它不是位于不同 URI 下的请求资源。而是完全不同的资源(登录页面与经过身份验证的内容页面)。

所以我继续前进并选择了另一个answer,令人惊讶的是另一个解决方案。

这个答案建议我们选择400 Bad Request

这个状态码的描述是:

由于语法错误,服务器无法理解该请求。客户端不应该不加修改地重复请求。

我认为服务器可以很好地理解请求,但只是在用户通过身份验证之前拒绝授予访问权限。

另一个answer 也表示403 的响应是正确的,但它的结尾是:

如果这是一个面向公众的网站,您试图根据会话 cookie [这就是我所做的] 拒绝访问,200 带有适当的正文以指示需要登录或 302 临时重定向到登录页面通常是最好的。

所以403 是正确的,但200302 是最好的。

嘿!这就是我要寻找的:最好的解决方案。但是最好的不应该和正确的一样吗?为什么它会是最好的?

感谢所有在这个问题上提出这么多的人:)

我知道我不应该为此担心太多。而且我认为这个问题更具假设性(不是真的,而是因为没有更好的词而使用它)。

但是这个问题已经困扰了我一段时间了。

如果我是一名经理(只是像往常一样选择一些听起来很酷的词),我会说:但是,但是,但是,但是放松很重要。 :-)

那么:the right way™ 在上述情况下使用状态码是什么(如果有的话)?

tl;dr

当用户尝试访问需要登录的页面时,正确的 http 状态代码响应是什么?

【问题讨论】:

  • 如果不是 401 则由您决定,然后是 200 或 302,我使用 200,我说检查最大的搜索提供商和社交网站的标题,看看那里返回的标题。
  • 412 Precondition Failed... 前提条件是通过 WWW-Auth 以外的方式向服务进行身份验证。这里没有最好的回应。我个人使用403 Forbidden,因为这就是正在发生的事情。 “授权将无济于事,不应重复请求。”指的是“WWW-Auth”。
  • 正确答案是418: I'm a teapot
  • 下面是如何正确使用 401 的方法,正如 user1093284 建议的那样:stackoverflow.com/a/19102200

标签: rest http-status-codes


【解决方案1】:

我相信 401 是从授权失败返回的正确状态码。参考RFC 2616 section-14.8 它读取“希望通过服务器验证自己的用户代理 - 通常但不一定在收到 401 响应后”

【讨论】:

    【解决方案2】:

    这里我不是在谈论 HTTP 身份验证,所以至少有 1 个状态码我们不会使用(401 Unauthorized)。

    错了。 401 是超文本传输​​协议(RFC 2616 Fielding 等)的一部分,但不限于 HTTP 身份验证。此外,它是唯一指示请求需要用户身份验证的状态代码。

    302 & 200 代码可以一些场景中使用并且更容易实现,但不是全部。如果你想遵守规范,401 是唯一正确的答案。

    而且 403 确实是最错误的返回码。正如你所说的那样......

    授权无济于事,不应重复请求。

    所以这显然不适合表明授权是一种选择。

    我会坚持标准:401 Unauthorized

    -

    更新

    添加更多信息,消除与...相关的困惑

    响应必须包含 WWW-Authenticate 标头字段(第 14.47 节),其中包含适用于所请求资源的质询。

    如果您认为这会阻止您使用 401,您必须记住还有更多:

    “该字段值包含至少一个质询,指示身份验证方案和适用于请求 URI 的参数。”

    这个“指示身份验证方案”意味着您可以选择加入其他身份验证方案!

    HTTP 协议 (RFC 2616) 为访问身份验证方案定义了一个简单的框架,但您不必使用该框架。

    换句话说:您不必使用通常的 WWW-Auth。您只需要说明您的 web 应用程序如何进行授权并在标头中提供相应的数据,仅此而已。根据规格,使用401,您可以选择自己的授权毒药!这就是您的“webapp”在涉及 401 标头和您的授权实现时可以执行您希望它执行的操作的地方。

    不要让规范让您感到困惑,以为您必须使用通常的 HTTP 身份验证方案。你没有!规范真正强制执行的唯一一件事:您只需/必须确定您的 web 应用程序的身份验证方案并传递相关参数以使请求方能够开始潜在的授权尝试。

    如果你仍然不确定,我可以把这一切放在一个简单但可以理解的角度:假设你明天要发明一个新的授权方案,那么规范也允许你使用它。如果规范限制了这种更新的授权技术实现的实现,那么这些规范就会在很久以前就被修改过。规范定义了标准,但它们并没有真正限制大量的潜在实现。

    【讨论】:

    • 这似乎是正确的,但正如规范中所述:The response MUST include a WWW-Authenticate header field (section 14.47) containing a challenge applicable to the requested resource. 但我有一个 webapp。
    • 你可以在标题中选择你自己的授权毒药!我在答案中添加了“更新”,以提供更多相关信息。 ;)
    • 显示一个 WWW-Authentication 标头的示例,您将在需要身份验证 cookie 的情况下将其与 401 一起使用?
    【解决方案3】:

    您依次要求“最好的”、“正确的方式”和“正确的”,这使得回答这个问题变得困难,因为这些标准不一定可以互换,实际上可能会发生冲突——尤其是在涉及到休息。

    “最佳”答案取决于您的应用程序。您是否正在构建基于普通旧浏览器 (POBB) 的 Web 应用程序?您是否正在构建本地客户端(例如 iOS 或 Android)并通过 Web 访问服务?您是否大量使用 AJAX 来驱动网页更新? curl 是预期的客户吗?

    假设您正在构建一个传统的 Web 应用程序。让我们看看 Google 是如何做到的(为简洁起见,将输出截断):

    $ curl -v http://gmail.com/
    < HTTP/1.1 301 Moved Permanently
    < Location: http://mail.google.com/mail/
    < Content-Type: text/html; charset=UTF-8
    < Content-Length: 225
    < ...
    

    Google 首先将我们重定向到 GMail 的“真实”网址(使用 302 重定向)。

    $ curl -v http://mail.google.com/mail/
    < HTTP/1.1 302 Moved Temporarily
    < Location: https://accounts.google.com/ServiceLogin?service=mail&passive=true&rm=false&continue=http://mail.google.com/mail/&scc=1&ltmpl=default&ltmplcache=2
    < Content-Type: text/html; charset=UTF-8
    < Content-Length: 352
    < ...
    

    然后它会将我们重定向到登录页面(使用 302 重定向)。

    $ curl -v 'https://accounts.google.com/ServiceLogin?service=mail&passive=true&rm=false&continue=http://mail.google.com/mail/&scc=1&ltmpl=default&ltmplcache=2'
    < HTTP/1.1 200 OK
    < Content-Type: text/html; charset=UTF-8
    < Transfer-Encoding: chunked
    < ...
    

    登录页面本身带有 200 状态码

    为什么会这样?

    从用户体验的角度来看,如果用户因为未通过身份验证而访问他们无法查看的页面,您希望将用户带到允许他们更正此问题的页面(通过登录)。在这个例子中,登录页面是独立的,只是另一个页面(这就是为什么 200 比较合适)。

    您可以抛出一个 4XX 页面,其中包含解释和登录页面的链接。事实上,这可能看起来更加 RESTful。但这是更糟糕的用户体验。

    好的,但是有没有像 403 这样的东西有意义的情况?绝对的。

    不过,首先请注意,403 在规范中没有明确定义。为了了解它应该如何使用,您需要查看它是如何在现场实现的。

    403 通常被 Apache 和 IIS 等 Web 服务器用作当浏览器请求目录列表(以“/”结尾的 URI)但服务器禁用目录列表时返回的页面的状态代码。在这种情况下,403 确实是一个专门的 404,除了让他/她知道出了什么问题,您无能为力。

    但是,这是一个使用 403 的网站示例,它既向用户发出信号表明他/她没有足够的权限以及采取什么措施来纠正这种情况(查看full response了解详情):

    curl -v http://www.w3.org/Protocols/rfc2616/
    < HTTP/1.1 403 Forbidden
    < Content-Type: text/html; charset=iso-8859-1
    < Content-Length: 1564
    < ...
    

    (顺便说一句,403 也出现在基于 Web 的 API 中,例如 Twitter 的 API;这里,403 表示“请求已被理解,但已被拒绝。随附的错误消息将解释原因。使用此代码当请求因更新限制而被拒绝时。")

    不过,作为一项改进,我们假设您不想将用户重定向到登录页面,或强制用户点击登录页面的链接。相反,您希望在阻止用户看到的页面上显示登录表单。如果他们成功验证,他们会在页面重新加载时看到内容;如果他们失败了,他们会再次获得登录表单。他们从不导航到另一个 URL。

    在这种情况下,状态码 403 很有意义,并且与 401 情况同源,但需要注意的是浏览器不会弹出要求用户进行身份验证的对话框——表单位于页面本身。

    这种身份验证方法并不常见,但它可能有意义,并且恕我直言,它比开发人员尝试实施的 pop-up-a-javascript-modal-to-log-in 解决方案更可取。

    问题归结为,你要重定向还是不重定向?

    补充:关于 401 状态码的想法...

    401 状态码(以及相关的基本/摘要式身份验证)有很多用途。它被 HTTP 规范所接受,每个主流浏览器都支持它,它并不是天生就不是 RESTful 的……问题是,从用户体验的角度来看,它非常没有吸引力。有没有样式的,神秘的弹出对话框,缺乏优雅的注销解决方案等。如果您(或您的利益相关者/客户)可以忍受这些问题(一个很大的如果),那么它可能有资格作为“正确" 解决方案。

    【讨论】:

    • 很好的解释和案例!
    • “401 状态码……非常非常没有吸引力。” - 澄清一下,401 并不一定意味着基本/摘要身份验证。触发浏览器“没有吸引力”的密码对话框的不是 401 状态。它是相应的WWW-Authenticate 标头,具有触发浏览器对话框的有效基本/摘要响应。例如,您可以返回带有 WWW-Authenticate: Custom realm="Login required" 标头的 401,并且浏览器不会干预,允许您以与发出 403 相同的方式继续操作。这是否仍然“正确”是另一回事?
    【解决方案4】:

    正如您所指出的,403 Forbidden 是用短语“授权无济于事”明确定义的,但值得注意的是,作者在这里几乎肯定指的是 HTTP 授权(这将确实无济于事,因为您的网站使用了不同的授权方案)。实际上,鉴于状态代码是给 user agent 而不是 user 的信号,只要代理尝试提供的任何授权都不会,这样的代码就是正确的进一步协助所需的授权过程(参见401 Unauthorized)。

    但是,如果您从字面上理解403 Forbidden 的定义并觉得它仍然不合适,那么409 Conflict 可能适用吗?定义在RFC 2616 §10.4.10:

    由于与当前的冲突,请求无法完成 资源的状态。仅在以下情况下才允许使用此代码 预计用户可能能够解决冲突 并重新提交请求。响应正文应该包含足够的 供用户识别冲突来源的信息。 理想情况下,响应实体将包含足够的信息 用户或用户代理来解决问题;但是,这可能不是 可能而且不是必需的。

    确实与资源的当前状态存在冲突:资源处于“锁定”状态,这种冲突只能通过用户提供凭据并重新提交请求来“解决”。正文将包括“足够的信息让用户识别冲突的来源”(它会声明他们没有登录)并且实际上还将包括“足够的信息用于用户或用户代理来解决问题”(即登录表单)。

    【讨论】:

      【解决方案5】:

      你的答案:

      401 Unauthorized 特别是如果您不关心或不会将人们重定向到登录页面

      -或-

      302 Found 表示存在资源,但他们需要提供凭据才能返回给它。仅当您将使用重定向并确保在响应正文中提供适当的信息时才执行此操作。


      其他建议:

      401 Unauthorized一般用于用户经过认证后无权访问的资源。

      403 Forbidden 老实说对我来说有点晦涩难懂。当我从文件系统级别锁定资源时,我会使用它,就像您的帖子所说的那样,“授权无济于事”。

      400 Bad Request 不合适,因为需要登录并不代表语法错误。

      【讨论】:

        【解决方案6】:

        您的“TL;DR”与“TL”版本不匹配。

        请求您需要授权才能请求的资源的正确响应是 401。

        302 不是正确的响应,因为事实上,资源在其他地方不可用。原始 URL 是正确的,客户端根本没有权限。如果您遵循重定向,您实际上并没有得到您正在寻找的东西。您会陷入与资源无关的特定工作流程。

        403 不正确。 403 是“无法从这里到达”错误。你根本看不到这一点,我不在乎你是谁。有些人会认为 403 和 404 是相似的。区别仅在于 403,服务器说“是的,我有,但你不能”,而 404 说“我对你在说什么一无所知”。安全专家会争辩说 404 更“安全”。为什么要告诉他们一些他们不需要知道的事情。

        您遇到的问题与 REST 或 HTTP 无关。您的问题是试图在客户端和服务器之间建立一些有状态的关系,最终通过一些 cookie 表现出来。整个资源 -> 302 -> 登录页面都是关于使用被称为 Web 浏览器的黑客的用户体验,它恰好既是库存形式,也是一个糟糕的 HTTP 客户端和一个糟糕的 REST 参与者。

        HTTP 有一个授权机制。授权标头。在通用浏览器中,围绕它的用户体验非常糟糕。所以没有人使用它。

        所以没有正确的 HTTP 响应(当然有,401,但不要/不能使用它)。没有正确的 REST 响应,因为 REST 通常依赖于底层协议(在本例中为 HTTP,但我们已经解决了这个问题)。

        所以。登录页面的 302 -> 200 就是她写的全部内容。这就是你得到的。如果您没有使用浏览器,或者通过 XHR 或其他一些自定义客户端完成所有操作,这将不是问题。您只需使用 Authorization 标头,遵循 HTTP 协议,并利用 DIGEST 或 AWS 使用的方案,就可以完成。然后,您可以使用适当的标准来回答此类问题。

        【讨论】:

          【解决方案7】:

          如果用户没有提供任何凭据并且您的 API 需要这些凭据,则返回 401 - Unauthorized。这将挑战客户这样做。通常很少有关于这种特殊情况的争论。

          如果用户提供了有效的凭据,但他们不足以访问所请求的资源(可能凭据用于免费增值帐户,但请求的资源仅适用于您的付费用户),您有考虑到一些 HTTP 代码定义的松散性,有几个选项:

          1. 返回403 - Forbidden。这更具描述性,通常被理解为,“提供的凭据有效,但仍不足以授予访问权限”
          2. 返回401 - Unauthorized。如果您对安全性有疑虑,您可能不想将上述 (1) 中返回的额外信息返回给客户端
          3. 返回401403,但在响应正文中提供有用的信息,描述访问被拒绝的原因。同样,如果它对攻击者有所帮助,这些信息可能比您想要提供的要多。

          就我个人而言,我一直使用 #1 来处理有效凭据已通过但与之关联的帐户无权访问所请求资源的情况。

          【讨论】:

          • 401 在这里不合适,除非您使用“协议级”身份验证方案,例如 HTTP BasicDigest auth。特别是,返回没有有效WWW-Authenticate 标头字段的401 响应明显违反RFC 2616, section 10.4.2
          • @IlmariKaronen - 那么你会建议什么而不是 401?
          • 403 Forbidden,尽管 RFC 中的措辞有些混乱,但这里是正确的选择。可以说,409 Conflict 也可能适合,但它似乎并不真正用于此用途。 RFC 2616 也(有点勉强)允许使用404 Not Found“当服务器不希望确切地揭示请求被拒绝的原因,或者没有其他响应适用时”,所以从技术上讲这也是有效的。当然,不会触发浏览器任何特殊处理的任何 4xx 系列代码在实践中可能会起作用,例如,418 I'm a teapot
          • 在讨论 RFC 合规性时,我完全同意您的看法。然而,更多的 RESTful API 正在设计中,它们将越来越丰富的语义值应用于现有的响应代码,我相信他们的需求现在超出了 HTTP 规范的预期。对我来说,这就像语言的演变,我不认为语言应该由字典来规定,只是描述。同样,我相信 RESTful API 可以而且应该有助于改进 HTTP 规范,而将某些(现有或发明的)代码用于新目的的实际使用可能会导致这种情况发生。
          • 好点;我没有注意到rest 标签。对于 REST API,有一个相当好的论据可以将 HTTP 仅仅视为一个哑传输层,并返回一个带有特定于 API 的有效负载的 200 OK 响应,表明需要进行身份验证。特别是,如果您希望使用例如访问 API,这或多或少是必要的。 JSONP。也就是说,即使 401 带有一个虚构的身份验证方案也可以在这种情况下工作,但只能在理解它的 API 特定客户端中工作。
          【解决方案8】:

          同意。 REST 只是一种风格,而不是严格的协议。许多公共网络服务偏离了这种风格。你可以建立你的服务来返回你想要的任何东西。只需确保您的客户了解预期的返回代码。

          就我个人而言,我一直使用 401(未经授权)来表示未经身份验证的用户请求了需要登录的资源。然后我要求客户端应用程序引导用户登录。

          我使用 400(错误请求)来响应使用无效凭据的登录尝试。

          HTTP 302(已移动)似乎更适合客户端为浏览器的 Web 应用程序。浏览器通常遵循响应中的重定向地址。这对于将用户引导至登录页面很有用。

          【讨论】:

          • 我同意你的观点,除了 400 状态代码部分。 400 是语法错误的请求('由于语法错误,服务器无法理解请求。'),而不是用户指定了错误的凭据。
          • 好点。查看 RFC,我看到它声明“由于语法错误,服务器无法理解请求。”所以 400 可能不是最好的响应。你会建议什么? 401?
          • 每次用户需要进行身份验证时我都使用 401,而当用户无法访问资源(没有权限)时使用 403。但是我已经阅读了很多关于这种不正确用法的意见,因为该规范提到 401 是 WWW-authentication 标头字段。我希望在未来的版本中 401 将代表一般身份验证/授权:)
          猜你喜欢
          • 2023-03-11
          • 1970-01-01
          • 1970-01-01
          • 2017-01-02
          • 2011-04-02
          • 1970-01-01
          • 2022-11-03
          • 2020-08-12
          • 2011-09-26
          相关资源
          最近更新 更多