如果用户试图访问要求用户登录的页面,则返回正确的http状态代码似乎存在很多混淆.
那么基本上当我显示登录页面时会发送什么状态代码?
我很确定我们需要使用该4xx
范围内的状态代码.
我不是在谈论HTTP身份验证,所以至少有一个我们不会使用的状态代码(401 Unauthorized
).
现在我们应该使用什么?答案(也在SO上)似乎有所不同:
根据这里的答案,我们应该使用403 Forbidden
.
但在状态代码的描述中是:
授权无效,请求不应重复.
嗯,这看起来不是正确的.由于授权WOULD帮助.
所以让我们看看其他答案.这里的答案甚至根本不使用4xx
范围而是使用302 Found
302 Found
状态代码的描述:
请求的资源暂时驻留在不同的URI下.由于重定向有时可能会被更改,因此客户端应该继续使用Request-URI来处理将来的请求.如果由Cache-Control或Expires头字段指示,则此响应仅可缓存.
我认为这也不是我想要的.因为它不是位于不同URI下的请求资源.而是一个完全不同的资源(登录页面与经过身份验证的内容页面).
所以我走了另一个解决方案令人惊讶地选择了另一个答案.
这个答案建议我们选择400 Bad Request
.
此状态代码的说明是:
由于语法格式错误,服务器无法理解请求.客户端不应该在没有修改的情况下重复请求.
我认为服务器理解请求很好,但在用户通过身份验证之前拒绝提供访问权限.
另一个答案也说403
答案是正确的,但它的结尾是:
如果这是一个面向公众的网站,你试图根据会话cookie拒绝访问[这就是我所做的],200使用适当的主体来指示需要登录或者302临时重定向到登录页面通常最好.
所以403
是正确的,但是200
或者302
是最好的.
嘿! 这就是我要找的:最好的解决方案.但最好不要和正确的一样吗?为什么它会是最好的?
感谢所有在这个问题上做到这一点的人:)
我知道我不应该太担心它.而且我认为这个问题更具假设性(不是真的,而是因为缺乏一个更好的词而使用它).
但这个问题现在困扰着我一段时间了.
如果我会成为一名经理(他们总是会听到一些很酷的发音),我会说:但是,但是,但是,但是,宁静很重要.:-)
那么:the right way™
在上述情况下使用状态代码是什么(如果有的话)?
TL;博士
当用户尝试访问需要登录的页面时,正确的http状态代码响应是什么?
如果用户未提供任何凭据且您的API需要它们,请返回a 401 - Unauthorized
.这将挑战客户这样做.关于这种特殊情况的争论通常很少.
如果用户提供了有效的凭据,但他们不足以访问所请求的资源(可能是凭证是免费增值帐户,但所请求的资源仅适用于您的付费用户),您可以选择以下几种选项. HTTP代码定义:
返回403 - Forbidden
.这更具描述性,通常被理解为"提供的凭据有效但仍不足以授予访问权限"
返回401 - Unauthorized
.如果您对安全性感到偏执,您可能不希望将额外信息返回给客户端,如上面(1)中返回的那样
在回报401
或403
但在描述为什么访问被拒绝的原因响应身体有帮助的信息.同样,这些信息可能比你想要提供的更多,以防它在某种程度上帮助攻击者.
就个人而言,我总是使用#1作为已通过有效凭据但与其关联的帐户无权访问所请求资源的情况.
反过来,你要求"最好的","正确的方式"和"正确的",这使得回答这个问题变得困难,因为这些标准不一定是可以互换的,事实上可能是冲突 - 特别是在涉及RESTfulness的地方.
"最佳"答案取决于您的申请.您是否正在构建基于纯旧浏览器(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<mpl=default<mplcache=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<mpl=default<mplcache=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向用户发出信号的网站示例,他/她没有足够的权限以及采取什么行动来纠正这种情况(详情请查看完整的响应):
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情况同源,但需要注意的是浏览器不会弹出一个要求用户进行身份验证的对话框 - 表单位于页面本身.
这种身份验证方法并不常见,但它可能有意义,并且恕我直言,开发人员尝试实现的弹出式javascript模式到登录解决方案.
它归结为这个问题,你想重定向还是不重定向?
附加: 关于401状态代码的想法......
401状态代码 - 以及相关的基本/摘要认证 - 有很多功能.它受到HTTP规范的支持,它得到了每个主流浏览器的支持,它本身并不是RESTful ......问题是,从用户体验的角度来看,它非常缺乏吸引力.有一个不可调整的,神秘的弹出式对话框,缺乏一个优雅的退出解决方案等等.如果你(或你的利益相关者/客户)可以忍受这些问题(一个大的if),那么它可能有资格作为"正确的"解决方案.
同意.REST只是一种风格,而不是严格的协议.许多公共Web服务偏离了这种风格.您可以构建您的服务以返回您想要的任何内容.只需确保您的客户知道预期返回代码的方式.
就个人而言,我一直使用401(未经授权)来指示未经身份验证的用户请求了需要登录的资源.然后,我要求客户端应用程序引导用户登录.
我使用400(错误请求)来响应具有无效凭据的登录尝试.
HTTP 302(移动)似乎更适合客户端是浏览器的Web应用程序.浏览器通常遵循响应中的重定向地址.这对于引导用户登录页面非常有用.
我不是在谈论HTTP身份验证,因此至少有一个我们不会使用的状态代码(401 Unauthorized).
错误.401是超文本传输协议(RFC 2616 Fielding,等人)的一部分,但不限于HTTP认证.此外,它是唯一的状态代码,表明请求需要用户身份验证.
可以使用302和200代码,并且在某些情况下更容易实现,但不是全部.如果你想遵守规范,401是唯一正确的答案.
而403确实是最错误的代码返回.正如你所说的......
授权无效,请求不应重复.
因此,这显然不适合表明授权是一种选择.
我会坚持标准:401 Unauthorized
-
UPDATE
添加更多信息,解除与...相关的困惑
响应必须包含WWW-Authenticate头字段(第14.47节),其中包含适用于所请求资源的质询.
如果你认为这会阻止你使用401,你必须记住还有更多:
"字段值至少包含一个挑战,指示适用于Request-URI的身份验证方案和参数."
这"表示身份验证方案"意味着您可以选择加入其他身份验证方案!
HTTP协议(RFC 2616)为访问认证方案定义了一个简单的框架,但您不必使用THAT框架.
换句话说:你不会受到通常的WWW-Auth的束缚.你只需要指出你的webapp如何进行授权并在标题中提供相应的数据,这就是全部.根据规格,使用401,您可以选择自己的授权毒药!这就是您的"webapp"可以执行您希望它在401标头和授权实现时执行的操作.
不要让规格混淆你,认为你必须使用通常的HTTP身份验证方案.你没有!规范唯一真正强制执行的是:您只需要/必须识别您的webapp的身份验证方案并传递相关参数,以使请求方能够启动潜在的授权尝试.
如果你仍然不确定,我可以把所有这些都放到一个简单但可以理解的角度来看:假设你明天要发明一个新的授权方案,那么规格允许你也使用它.如果规范限制了这种较新的授权技术实现的实现,那么这些规范将在很久以前被修改过.规范定义了标准,但它们并没有真正限制大量潜在的实现.