Symfony2 防止多个表单提交

Posted

技术标签:

【中文标题】Symfony2 防止多个表单提交【英文标题】:Symfony2 prevent multiple form submission 【发布时间】:2014-09-04 05:42:54 【问题描述】:

我正在使用 Symfony2 制作一个表单。感谢 form_rest(myform) ,我有一些实体字段和正确呈现的csrf 令牌。

问题是:

    用户填写表单并单击提交按钮(然后发布表单) 用户快速按退出键 用户再次点击提交按钮(表单再次发布)

结果:一个实体(表单绑定到一个实体)在数据库中插入了两次

这可以无限发生

我认为使用 CSRF 令牌字段可以防止这种情况,但事实并非如此。那么有什么方法可以用 Symfony 框架本地解决这个问题吗? 如果不是还有什么可能?

提前谢谢你!

【问题讨论】:

【参考方案1】:

利用 Symfony 的另一种方法是在表单视图控制器上实现提交重定向到表单处理器控制器。

这通过接收表单值一次并在收到值后立即重定向用户来工作。 因此,如果该操作被取消并重新提交或多次单击,则初始请求无法将用户重定向到表单处理器。有效防止多次提交数据,因为重定向响应被返回给用户并且没有调用表单处理器。

使用状态码307 的重定向充当快捷方式,允许将整个请求传递给另一个控制器方法并保留请求方法类型和数据。同时,用户不会在视觉上注意到变化,也不会收到无效的 CSRF 令牌错误。

namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

class DefaultController extends Controller


    /**
     * @Route('/submit', name="submit")
     */
    public function submitAction(Request $request)
    
       if ($request->isMethod($request::METHOD_POST))    
          return $this->redirectToRoute('process', ['request' => $request], 307);
       
       //... form view
       $form = $this->createForm(FormType::class, $data, ['action' => $this->generateUrl('submit')]);
       $form->handleRequest($request);
       /** 
        * alternative to comparing the request method type above
        * if ($form->isSubmitted()) 
        *    return $this->redirectToRoute('process', ['request' => $request], 307);
        * 
        */
       return $this->render('::form_view', [
          'form' => $form->createView()
       ]);
    

    /**
     * @Route('/process', name="process")
     */
    public function processAction(Request $request)
    
       $form = $this->createForm(FormType::class, $data, ['action' => $this->generateUrl('submit')]);
       $form->handleRequest($request);
       if ($form->isSubmitted() && $form->isValid()) 
           //... process form
           $this->addFlash('success', 'Form Processed!');
           return $this->redirectToRoute('submit');
       

       //... show form errors and allow resubmission
       return $this->render('::form_view', [
          'form' => $form->createView()
       ]);
    

【讨论】:

【参考方案2】:

我不确定这是否是正确的方法,但您可以执行以下操作。

在您的FormType 中,设置以下选项:

对于 Symfony intention:

public function configureOptions(OptionsResolver $resolver)

    $resolver->setDefaults([
        'csrf_protection' => true,
        'csrf_field_name' => '_token',
        // important part; unique key
        'intention'       => 'form_intention',
    ]);

对于 Symfony >= 4 使用 csrf_token_id:

public function configureOptions(OptionsResolver $resolver)

    $resolver->setDefaults([
        'csrf_protection' => true,
        'csrf_field_name' => '_token',
        // important part; unique key
        'csrf_token_id'   => 'form_intention',
    ]);

然后在您的控制器操作中,您可以使用 intentioncsrf_token_id 执行类似的操作:

if ($form->isSubmitted()) 
    // refresh CSRF token (form_intention)
    $this->get("security.csrf.token_manager")->refreshToken("form_intention");

这可以防止给定表单的重复提交。

【讨论】:

是的,它会像魅力一样检测多次提交。但是现在你必须处理 invalidCSRF-Token。 if($form->isSubmitted() && !$form->isValid()) $submittedToken = $request->request->get('token'); if (!$this->isCsrfTokenValid('token_id', $submittedToken)) return $this->redirectToRoute('home'); //如果提交句柄完成,做你想做的事情。例如重定向 我在 Symfony v3.4.49 上,csrf_token_id 为我工作。你确定“对于 Symfony >= 4”正确吗?【参考方案3】:

CSRF 令牌在会话期间不会更改,因此您不能使用它来阻止多个表单提交。

你可以在字体端使用 javascript 来解决这个问题,例如this question 基本上你在按下提交按钮后禁用它。

另见http://technoesis.net/prevent-double-form-submission/

【讨论】:

以上是关于Symfony2 防止多个表单提交的主要内容,如果未能解决你的问题,请参考以下文章

如何防止在 PHP 中重复提交表单(跨多个窗口进行保护)?

如何防止在 PHP 中多次单击时提交多个表单

如何在 symfony2 中提交表单 ajax?

防止表单数据无效时单击多个(提交)按钮(vee-validate)

如何防止 CakePHP 3.0 中的多个表单提交?

无法使用 Symfony2 和 TinyMCE 提交表单