Django Forbidden 403 Error "CSRF token missing or incorrect" 在已经登录的情况下尝试登录
Django Forbidden 403 Error "CSRF token missing or incorrect" when trying to log in when already logged in
所以我使用所有内置的 Django,我没有自己的登录视图。
首先我将描述导致此错误的场景是如何创建的:
所以有人登录并打开了多个选项卡,然后他们注销了。最终,其他选项卡将自己定向到 accounts/login 页面,或者刷新页面或按下 link,这并不重要。关键是这个人现在有多个选项卡,都在 accounts/login 屏幕上,没有人在 authenticated/logged 中。
那么此人成功登录了其中一个选项卡。然后他们转到其他选项卡之一,这些选项卡仍在 accounts/login 页面上(即使已经创建了一个会话)。
如果此人随后尝试以登录人或任何人的身份登录,实际上即使用户名和密码为空并且他们只是单击登录按钮,也会抛出 403 Error CSRF token missing or incorrect
。
(我希望这是有道理的)
无论如何,我该如何解决这个问题?
如果有帮助,这是我的 html 模板:
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class='panel panel-primary' style='margin: 0 auto; max-width: 360px;'>
<div class='panel-heading'>
<h3 class='panel-title'>Login</h3>
</div>
<div class='panel-body'>
<form action='{% url 'login' %}' novalidate='novalidate' role='form' method='post'>
{% csrf_token %}
{{ form|crispy }}
<input type='hidden' name='next' value='{{ next }}' />
<div class='form-group' style='margin-bottom: 0;'>
<p>
<small>
By using this site, you agree to our
<a href='{% url 'privacy-policy' %}'>privacy policy</a>
and
<a href="{% url 'terms-of-use' %}">terms of use</a>.
</small>
</p>
<button class='btn btn-primary' type='submit'>Login</button>
</div>
</form>
</div>
</div>
{% endblock %}
编辑:
我还想补充一点,如果我创建相同的场景而不是立即尝试登录,而是只重新加载页面一次,则不会发生 403 错误。
我只是希望能够防止用户在使用该网站时 运行 出现错误(当然,除了直接在搜索栏中输入内容之外)。一个想法可能是,当点击登录时,它会像刷新页面一样刷新 csrf 令牌,这可能是个好主意吗?
它会发生,因为出于安全原因,每次用户登录时都会更改密码的值。
- 在每个 Django 表单中,您都有一个名为 "CSRF token" 或
{% csrf_token %}
的隐藏输入。
该值是在表单生成时随机且唯一生成的
呈现并将在发出请求后进行比较。所以
请求只能从授权用户的浏览器发送。有
攻击者无法(据我所知)获得此令牌并执行
Django后端可以接受的恶意请求。
For all incoming requests that are not using HTTP GET, HEAD, OPTIONS
or TRACE, a CSRF cookie must be present, and the ‘csrfmiddlewaretoken’
field must be present and correct. If it isn’t, the user will get a
403 error.
When validating the ‘csrfmiddlewaretoken’ field value, only the secret, not the full token, is compared with the secret in the cookie
value. This allows the use of ever-changing tokens. While each request
may use its own token, the secret remains common to all. This check is
done by CsrfViewMiddleware.
了解更多详情click here
嗯,看来我找到了一个解决方案,虽然不能完全直接解决错误,但确实可以避免发生错误的情况。行为有点不同,但对我来说效果很好,我认为其他人大部分时间都是一样的。
所以首先,只要用户已经登录,我就会从登录页面重新路由。我通过创建自己的登录视图来完成此操作:
class LoginView(LoginView):
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated:
return HttpResponseRedirect(request.GET.get("next") or "/")
else:
return super().dispatch(request, *args, **kwargs)
其次,每次 window 变为 "focused"(即切换到选项卡)时,都会发送一个简单的 ajax 请求来检查用户是否已通过身份验证,如果是,window 将被重新加载,从而将它们带到正确的页面。
为此,我将 javascript 添加到 login.html
页面:
{% block extrascripts %}
{{ block.super }}
<script>
"use strict";
window.onfocus = function () {
$.ajax({
url: "/accounts/status/",
success: function(result, status, xhr) {
if (result.is_authenticated) {
window.location.reload();
}
}
});
};
</script>
{% endblock %}
并添加了这个视图:
@method_decorator(csrf_exempt, name="get")
class StatusView(View):
def get(self, request, *args, **kwargs):
return JsonResponse({"is_authenticated": request.user.is_authenticated})
当然,我也更新了 URL。
我还要补充一点,如果您不想在已经通过身份验证后被重定向离开登录页面,您可以将该代码保留在外面,它应该仍然可以修复错误,考虑到我没有遇到错误我什么时候会在尝试登录之前先重新加载页面。
所以我使用所有内置的 Django,我没有自己的登录视图。
首先我将描述导致此错误的场景是如何创建的:
所以有人登录并打开了多个选项卡,然后他们注销了。最终,其他选项卡将自己定向到 accounts/login 页面,或者刷新页面或按下 link,这并不重要。关键是这个人现在有多个选项卡,都在 accounts/login 屏幕上,没有人在 authenticated/logged 中。
那么此人成功登录了其中一个选项卡。然后他们转到其他选项卡之一,这些选项卡仍在 accounts/login 页面上(即使已经创建了一个会话)。
如果此人随后尝试以登录人或任何人的身份登录,实际上即使用户名和密码为空并且他们只是单击登录按钮,也会抛出 403 Error CSRF token missing or incorrect
。
(我希望这是有道理的)
无论如何,我该如何解决这个问题?
如果有帮助,这是我的 html 模板:
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class='panel panel-primary' style='margin: 0 auto; max-width: 360px;'>
<div class='panel-heading'>
<h3 class='panel-title'>Login</h3>
</div>
<div class='panel-body'>
<form action='{% url 'login' %}' novalidate='novalidate' role='form' method='post'>
{% csrf_token %}
{{ form|crispy }}
<input type='hidden' name='next' value='{{ next }}' />
<div class='form-group' style='margin-bottom: 0;'>
<p>
<small>
By using this site, you agree to our
<a href='{% url 'privacy-policy' %}'>privacy policy</a>
and
<a href="{% url 'terms-of-use' %}">terms of use</a>.
</small>
</p>
<button class='btn btn-primary' type='submit'>Login</button>
</div>
</form>
</div>
</div>
{% endblock %}
编辑:
我还想补充一点,如果我创建相同的场景而不是立即尝试登录,而是只重新加载页面一次,则不会发生 403 错误。
我只是希望能够防止用户在使用该网站时 运行 出现错误(当然,除了直接在搜索栏中输入内容之外)。一个想法可能是,当点击登录时,它会像刷新页面一样刷新 csrf 令牌,这可能是个好主意吗?
它会发生,因为出于安全原因,每次用户登录时都会更改密码的值。
- 在每个 Django 表单中,您都有一个名为 "CSRF token" 或
{% csrf_token %}
的隐藏输入。 该值是在表单生成时随机且唯一生成的 呈现并将在发出请求后进行比较。所以 请求只能从授权用户的浏览器发送。有 攻击者无法(据我所知)获得此令牌并执行 Django后端可以接受的恶意请求。
For all incoming requests that are not using HTTP GET, HEAD, OPTIONS or TRACE, a CSRF cookie must be present, and the ‘csrfmiddlewaretoken’ field must be present and correct. If it isn’t, the user will get a 403 error. When validating the ‘csrfmiddlewaretoken’ field value, only the secret, not the full token, is compared with the secret in the cookie value. This allows the use of ever-changing tokens. While each request may use its own token, the secret remains common to all. This check is done by CsrfViewMiddleware.
了解更多详情click here
嗯,看来我找到了一个解决方案,虽然不能完全直接解决错误,但确实可以避免发生错误的情况。行为有点不同,但对我来说效果很好,我认为其他人大部分时间都是一样的。
所以首先,只要用户已经登录,我就会从登录页面重新路由。我通过创建自己的登录视图来完成此操作:
class LoginView(LoginView):
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated:
return HttpResponseRedirect(request.GET.get("next") or "/")
else:
return super().dispatch(request, *args, **kwargs)
其次,每次 window 变为 "focused"(即切换到选项卡)时,都会发送一个简单的 ajax 请求来检查用户是否已通过身份验证,如果是,window 将被重新加载,从而将它们带到正确的页面。
为此,我将 javascript 添加到 login.html
页面:
{% block extrascripts %}
{{ block.super }}
<script>
"use strict";
window.onfocus = function () {
$.ajax({
url: "/accounts/status/",
success: function(result, status, xhr) {
if (result.is_authenticated) {
window.location.reload();
}
}
});
};
</script>
{% endblock %}
并添加了这个视图:
@method_decorator(csrf_exempt, name="get")
class StatusView(View):
def get(self, request, *args, **kwargs):
return JsonResponse({"is_authenticated": request.user.is_authenticated})
当然,我也更新了 URL。
我还要补充一点,如果您不想在已经通过身份验证后被重定向离开登录页面,您可以将该代码保留在外面,它应该仍然可以修复错误,考虑到我没有遇到错误我什么时候会在尝试登录之前先重新加载页面。