Django Rest Framework - 为啥在尝试使用不正确的凭据登录用户时返回 200 状态码?

Posted

技术标签:

【中文标题】Django Rest Framework - 为啥在尝试使用不正确的凭据登录用户时返回 200 状态码?【英文标题】:Django Rest Framework - Why is a 200 status code returned when trying to login a user using incorrect credentials?Django Rest Framework - 为什么在尝试使用不正确的凭据登录用户时返回 200 状态码? 【发布时间】:2015-12-20 03:26:16 【问题描述】:

这是我的 URLs.py:

url(r'^api-auth/', include('rest_framework.urls',
                               namespace='rest_framework')),

我的主页上有一个表单,用户可以在其中输入用户名和密码。当点击提交按钮时,AngularJS 会使用用户对象(用户名和密码)向“api-auth/login/”发送一个 POST 请求:

$http.post("/api-auth/login/", self.loginuser)
    .error(function(data, status, headers, config) 
        console.log(data);
     );

当用户提交不正确的用户名和密码(用户名和密码不存在或不匹配)时,Django Rest Framework 返回 200 OK 而不是 204 No Content、404 或 401 Unauthorized(在这篇文章中,它说 401 是要返回的正确状态代码:What's the appropriate HTTP status code to return if a user tries logging in with an incorrect username / password, but correct format?)。

根据此处:http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html 在第 9.5 节 POST 中,它说“在这种情况下,200(OK)或 204(无内容)是适当的响应状态,具体取决于响应是否包含以下实体描述结果。”

如果数据存在,我会处理错误并记录数据(我在 JS 中做了 console.log(data)),但没有记录数据,这意味着(根据我的理解)没有发送数据/响应不包含描述结果的实体。

那么 DjangoRestFramework 怎么会返回 200 而不是 204 No Content(或 404 或 401,根据我链接到的其他 SO 帖子应该返回的内容)?

【问题讨论】:

出了点问题,如果用户提交了错误或不正确的用户名/密码,您应该返回 404 或 400,而不是 204。 @levi 没错。我不期望 400,因为语法很好,服务器可以理解发送的用户名和密码,但我至少期望 404 而不是 200 或 204。 请注意,有一个新的 RFC 已经过时了您引用的那个。看看 7231。 @PabloPalácios 嗯,当您提到查看 7231 时,您指的是哪个文件?我去了这个站点:w3.org/Protocols 和 RFC 7231 导致了这个:tools.ietf.org/html/rfc7231(它与语义和内容有关)。它在哪里提到如何处理 POST 请求? 如前所述,请小心。新 RFC 中的 POST 方法可能没有重大变化。但是先看一下并更新您的问题并不需要花费。 【参考方案1】:

您的问题中有几件事混淆了。首先是您使用的技术观点,其次是您解释答案的方式。

1) 观点

这是一个很快的。您通过将数据发送到 /api-auth/login/ 它使用的视图 DRF 的登录视图用于可浏览的 API。这个视图实际上是 Django 的 auth 应用程序 (django.contrib.auth.views.login) 附带的视图,它假设它正在与用户打交道,手动浏览 API。

即:用GET 调用它会构建一个空的html 表单,用POST 发送回表单将触发表单验证,最终可能导致表单重新显示(200 Ok,包含文档),或要发回的重定向(302 Found with empty content)。

这就是你的数据为空的原因:服务器发送一个 HTML 文档,而你的 Angular 可能试图解析一些 JSON 对象。

您使用的表单视图绝对不打算从脚本中调用。这可以做到,但是您需要相应地处理结果,这意味着分析返回的 html 页面以查找错误消息。凌乱。

如果您想从脚本轻松访问它,您应该构建自己的登录视图。

2) 文档与请求

您在这里处理两个不同级别的语义。

    请求的含义。 请求中包含的文档的含义。

HTTP 错误代码在请求的上下文中是有意义的。它们发生在较低的水平。例如,返回 401 代码意味着“在执行此请求之前需要有效的身份验证凭据”

所以在这里,这基本上意味着“您必须拥有有效的身份验证凭据我处理您的登录请求之前”

这可能有意义,但仅在您有两层身份验证的情况下。您需要拥有对第一层有效的身份验证凭据,然后它才能让您的第二层登录请求通过。在这种情况下,如果您尝试使用第二层登录而第一层无法识别,您可能会收到 401。

那么,REST 如何适应?

REST 的概念,在应用于 HTTP 时,是为了尝试匹配请求级语义和文档级语义。它特别适合,因为每个 REST 概念都有一个匹配的 HTTP 动词,HTTP 是可缓存的,客户端-服务器,......以及......无状态

无状态意味着 HTTP 和 REST 都没有登录的概念。登录是一种抽象,这通常意味着我们使用如下所示的工作流:

    我们向某个端点(登录名/密码、质询、oauth 等)进行身份验证。 端点返回一些授权令牌 我们将授权令牌与每个下一个请求一起发送到服务器。

但事实是,每个请求都必须得到服务器的授权。也就是说,服务器总是先授权请求,然后再查看里面的内容。如果此步骤失败,则 401 是一个足够的响应。

除了第 1 步。此请求没有授权步骤。必须对其进行处理,必须检索和检查登录名/密码,并且根据结果,服务器可能会决定发回授权令牌。

那么,如果它选择不这样做,哪些错误代码是合适的?好吧,您可以选择以下几种:

200 好的。登录请求已成功处理并产生错误消息,这是一个包含结果的文档。然后,您的脚本将读取文档(例如可能是 JSON 对象)以查看它是否有错误或授权令牌。 204 无内容。登录请求已成功处理,但没有产生任何结果,当然也没有授权令牌。奇怪的选择,但正确。 400 错误请求。登录请求未成功处理。

唯一确定的是,401 不是一个选项。 401 意味着您不能尝试登录。不是登录失败。

【讨论】:

您认为使用 422(不可处理的实体)作为选项吗?服务器无法根据您提交的实体(凭据)处理您的登录请求。 @MichaelTsang> 响应码422,对吧?从 RFC 的上下文来看,它的意图似乎是用语义发出错误信号。它给出了格式正确的 XML 文件的示例,其内容没有意义。我猜想 422 可能是一个合适的选择,例如对于缺少字段的登录请求,但对于不正确的密码,它是可疑的。毕竟,服务器确实处理了登录请求,得出密码不正确的结论。 这里说 401 是正确的:***.com/questions/32752578/… @RoaflinSabos> 它给出的原因是不正确的,并且在这个答案中特别提到:它混淆了传输和内容。【参考方案2】:

在我看来,您在这里将业务概念与协议问题混为一谈。

登录方法不应返回 401(未授权),因为它的先决条件是用户(显然)尚未授权/验证。因此,如果请求以正确的方式(从语法上讲)发出,尽管用户凭据不正确(业务概念),响应应该是 200(协议),即请求被接受并正确处理。当然,响应正文将确定它是否成功登录。

所以,毕竟,当它实际上是业务层错误(用户根据您的数据库输入的值不正确)时,您正在尝试记录应用程序错误。明白了吗?

【讨论】:

但根据 RFC 7235:tools.ietf.org/html/rfc7235#section-3.1 它说“如果请求包括身份验证凭据,则 401 响应表明这些凭据的授权已被拒绝。用户代理可以使用新的或替换的授权头字段。”那么如果请求包含被拒绝的身份验证凭据,那么 401 不是正确的响应吗? 不,因为我们讨论的是两种不同类型的请求。例如,登录请求不能被视为获取数据的任何其他请求,因为它在某种程度上是身份验证过程的主要部分。例如,登录请求在其标头中不包含身份验证凭据,但在其正文中。因此,您不能取消对登录请求的授权,因为从本质上讲,它始终是每个人都可以访问的请求。所以登录响应的作用是判断凭据是否正常,而不是取消对请求的授权。 @user2719875 返回 401 意味着用户必须先登录才能访问登录表单。 @spectras,谢谢。这正是我的意思,但简而言之:)【参考方案3】:

如果您查看DRF's source,您会发现它使用了 Django 自己的登录/注销视图。因此,这个问题与 Django 的表单处理本身更相关。这是与 Django 本身相关的a question。

【讨论】:

以上是关于Django Rest Framework - 为啥在尝试使用不正确的凭据登录用户时返回 200 状态码?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 django-rest-framework 中为 API 使用 TokenAuthentication

如何仅使用 django 作为后端并使用 django-rest-framework 发布

如何在 Django Rest Framework 的 REST API 中为数组数据编写序列化程序?

我可以将单个 Django 模型字段拆分为多个 Django Rest Framework 序列化器字段吗?

在 Django REST Framework 中将 GET 参数转换为 Request 对象上的 POST 数据

如何在 Django Rest Framework 中将当前用户设置为用户字段?