403s 登录后重新提交表单

403s resubmitting a form after login

我在以下重现步骤下得到了 403:

  1. 注销后,尝试提交生成验证错误的 Django 表单
  2. 登录或注册有效帐户
  3. 使用浏览器返回验证错误的页面
  4. 重新提交表单

结果:403错误。这很可能是预期的行为,但我正在寻找一种更优雅的方式来处理这个问题。有什么好的方法可以捕捉到这一点并以登录用户身份重新提交表单?

嗯,是的,这是预期的行为。当您登录时,会生成新的 csrf_token。当您导航回出现验证错误的页面时,它仍然包含 <input type="hidden" name="csrfmiddlewaretoken" value="old_token" /> 中的旧 csrf_token。所以你提交无效的表单 csrf_token 并得到 403 错误。

我可以为您推荐两个选项(none 个我喜欢)

  1. 登录时禁用新的 csrf_token 生成。只需在您的登录视图中将 request.META['CSRF_COOKIE_USED'] = False 放在 login(request, user) 之后。

  2. 禁用 settings.py 的 csrf 保护 via decorator for your single view, or globally by removing csrf middleware

我在很多框架的上下文中看到过这个问题,唯一优雅的解决方案是JavaScript。

使用 JavaScript,您可以将输入值存储在 localStorage 中。然后在成功的表单提交事件中,清除这些值。如果表单加载了 localStorage 中存在的那些值(表单提交返回 403,用户返回表单页面),则自动使用这些值填充表单。

实施起来并不复杂,只是需要更多的工作。我相信有基于这个想法的 JS 库...

  1. 为所有表单元素指定一个类名。在示例中,我将使用 store-data。如果您在 django 中定义表单,则可以在 forms.Widget.attrs 中设置,或者如果您编写自己的 html.

    [=51,则只需在输入元素上使用 class 属性=]
  2. 提交后,将名为 formData 的项目添加到 localStorageformData 是一个 JS 对象映射表单字段元素 ids,从上面的类名到元素值。

  3. 如果表单被提交并处理为有效,在重定向页面上从 localStorage 中删除 formDatalocalStorage.removeItem()

  4. 加载表单页面时(这将是用户在 403 之后返回到表单的地方),如果 formData 存在于 localStorage 中,则将值加载到表单字段。

这是一个实现了此功能的示例表单:

<form name="myForm" action="{% url 'myapp:form_submit' %}" onsubmit="return storeData()">
    <label>Name: </label>
    <input type="text" class="store-data" id="inputName" />

    <label>Description: </label>
    <textarea class="store-data" id="textareaDescription"></textarea>

    <input type="submit" />
</form>
<script>
function storeData() {
    var elements = document.getElementsByClassName("store-data");
    var formData = {};

    // store element ids and values in formData obj
    for (var i = 0; i < elements.length; i++) {
        formData[elements[i].id] = elements[i].value;
    }

    // store formData to localStorage as string
    localStorage.setItem('formData', JSON.stringify(formData));
}

// if the localStorage item has already been set, then the user tried to submit and failed
if (localStorage.getItem('formData')) {
    formData = JSON.parse(localStorage.getItem('formData'))
    // set all the form elements to the values that were stored when the user tried to submit
    for (var key in formData) {
        document.getElementById(key).value = formData[key];
    }
}
</script>

并且在重定向成功页面上,一定要删除 formData 项。否则,任何时候用户返回表单时,值都将加载到字段中。 (我想这可能是一种理想的行为,但我对此表示怀疑。)

<script>
localStorage.removeItem('formData');
</script>