18

我试图找到一种方法来防止用户重复提交我的表单。我有禁用提交按钮的 javascript,但仍然有偶尔的用户找到双重提交的方法。

我有一个可重用的库的愿景,我可以创建它来保护它。

在我理想的库中,代码块看起来像这样:

try:
    with acquire_lock({'field1':'abc', 'field2':'def'}) as lock:
        response = #do some credit card processing
        lock.response = response
except SubmissionWasDuplicate, e:
    response = e.response

锁表看起来像这样:

duplicate_submission_locks

  • submit_hash # 提交参数的 MD5
  • response # 腌制数据
  • created_at # 用于扫这张表
  • lock_expired # 布尔值,表示锁是否已过期

有谁知道这是否已经存在?它似乎不难写,所以如果它不存在,我可以自己写。

4

5 回答 5

12

您可以使用会话来存储哈希

import hashlib

def contact(request):
    if request.method == 'POST':
        form = MyForm(request.POST)
        #join all the fields in one string
        hashstring=hashlib.sha1(fieldsstring)
        if request.session.get('sesionform')!=hashstring:
            if form.is_valid() :                                         
                request.session['sesionform'] = hashstring
                #do some stuff...
                return HttpResponseRedirect('/thanks/') # Redirect after POST  
        else
           raise SubmissionWasDuplicate("duplicate")
    else:
        form = MyForm() 

使用这种方法(不删除会话cookie),用户无法在会话过期时重新存储数据,顺便说一句,我假设存在识别发送数据的用户的东西

于 2010-01-28T20:24:25.587 回答
6

这个问题的一个简单解决方案是为每个表单添加一个唯一的哈希。然后你可以有一个当前表格的滚动表。当提交表单或哈希值太旧时,您可以将其从您的表中过期,并拒绝任何在您的表中没有匹配哈希的表单。

如前所述,HTTPRedirect 是正确的方法。

不幸的是,即使是 Django 自己的内置管理员也容易出现与此问题相关的问题。在某些情况下,跨站点脚本框架可以帮助防止其中一些,但恐怕当前的生产版本只是没有内置这个。

于 2010-01-26T07:48:27.993 回答
3

老实说,您最好的选择(简单且良好的做法)是向感谢页面发出 HTTPRedirect(),如果感谢页面与表单相同,那没关系。你仍然可以这样做。

于 2010-01-26T05:19:15.930 回答
3

Kristian Damian 的回答确实是一个很好的建议。我只是想到了该主题的细微变化,但它可能会产生更多开销。

You could try implementing something that is used in django-piston for BaseHandler objects, which is a method called exists() that checks to see if what you are submitting is already in the database.

From handler.py (BaseHandler):

def exists(self, **kwargs):
    if not self.has_model():
        raise NotImplementedError

    try:
        self.model.objects.get(**kwargs)
        return True
    except self.model.DoesNotExist:
        return False

So let's say make that a function called request_exists(), instead of a method:

if form.is_valid()
    if request_exists(request):
        # gracefully reject dupe submission
    else:
        # do stuff to save the request
        ...
        # and ALWAYS redirect after a POST!!
        return HttpResponseRedirect('/thanks/') 
于 2010-02-04T16:41:20.897 回答
2

使用 redirect-after-post 方法总是好的。这可以防止用户使用浏览器的刷新功能意外地重新提交表单。即使您使用哈希方法,它也很有帮助。这是因为在 POST 之后没有重定向,在点击返回/刷新按钮的情况下,用户会看到一个关于重新提交表单的问题消息,这会让她感到困惑。

如果您在每次 POST 后执行 GET 重定向,则点击返回/刷新将不会显示此奇怪的(对于普通用户)消息。因此,为了获得全面保护,请使用 Hash+redirect-after-post。

于 2010-01-27T13:23:43.483 回答