Symfony2阻止多个表单提交

时间:2014-07-14 10:19:53

标签: forms symfony submit csrf

我正在使用Symfony2处理表单。由于{{ form_rest(myform) }},我有一些实体字段和一个正确呈现的csrf标记。

问题是:

  1. 用户填写表单并点击提交按钮(然后发布表单)
  2. 用户快速按退出键
  3. 用户再次点击提交按钮(表单再次发布)
  4. 结果:实体(表单绑定到实体)在数据库中插入两次

    这可以无限发生

    我认为使用CSRF令牌字段可以防止出现这种情况,但事实并非如此。那么有没有办法用Symfony框架本地解决它? 如果没有可能存在的可能性?

    提前谢谢!

3 个答案:

答案 0 :(得分:3)

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

FormType中,设置以下选项:

对于Symfony< 4使用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");
}

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

答案 1 :(得分:2)

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

您可以使用Javascript在font-end中克服此问题,例如参见this question基本上您在按一次后禁用提交按钮。

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

答案 2 :(得分:0)

利用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()
       ]);
    }
}