CSRF 令牌无效。请尝试重新提交表单。 Symfony 4

Posted

技术标签:

【中文标题】CSRF 令牌无效。请尝试重新提交表单。 Symfony 4【英文标题】:The CSRF token is invalid. Please try to resubmit the form. Symfony 4 【发布时间】:2020-07-27 01:18:43 【问题描述】:

在我的表单中,用户必须选择一个选项,然后根据它选择用户。

每次尝试提交表单时都会出错。CSRF 令牌无效。请尝试重新提交表单。

我尝试使用 form_row(form._token) ,但它不起作用。 Symfony 告诉我 csrf 令牌的值是空的。

我的看法:

<div class="card">
  <div class="card-body">
      form_start(form) 
          form_rest(form) 
         <button class="btn btn-info">Envoyer</button>
       form_end(form) 
   </div>
</div>
    <script>
        $(document).on('change', '#bsv_send_cultures', function () 
            let $field = $(this)
            let $form = $field.closest('form')
            let data = 
            data[$field.attr('name')] = $field.val()
            $.post($form.attr('action'), data).then(function (data) 
                let $input = $(data).find('#bsv_send_user')
                $('#bsv_send_user').replaceWith( $input )
                $('#bsv_send_user').append( "<input id=\"selectAll\" type=\"checkbox\"><label for='selectAll'>Sélectioner tous</label>" )
                $("#selectAll").click(function()
                    $("input[type=checkbox]").prop('checked', $(this).prop('checked'));

                );
            )
        )
    </script>

我的表单生成器

class BsvSendType extends AbstractType

    public function buildForm(FormBuilderInterface $builder, array $options)
    
        $builder
            ->add('cultures', EntityType::class, [
                'class' => IndexCultures::class,
                'choice_label' => 'name',
                'mapped' => false,
                'required' => false,
                'placeholder' => 'Sélectionnez une culture',
                'attr' => [
                    'class' => 'select2'
                ]
            ])
            ->add('display_at', DateType::class, [
                'widget' => 'single_text',
                'html5' => false,
                'mapped' => false,
                'required' => false,
                'attr' => [
                    'class' => 'js-datepicker',
                    'autocomplete' => 'off'
                ],
                'label' => 'Date d\'envoi',
                'help' => 'Remplir uniquement en cas d\'envoi différé.'
            ])
        ;

        $builder->get( 'cultures')->addEventListener(
            FormEvents::POST_SUBMIT,
            function (FormEvent $event) 
                $form = $event->getForm();
                $this->addUserField( $form->getParent(), $form->getData());
            
        );

        $builder->addEventListener(
            FormEvents::POST_SET_DATA,
            function (FormEvent $event) 
                $form = $event->getForm();
                $this->addUserField( $form, null );
            
        );
    

    /**
     * @param FormInterface $form
     * @param IndexCultures|null $indexCultures
     */
    private function addUserField(FormInterface $form, ?IndexCultures $indexCultures)
    
        if (is_null($indexCultures)) 
            $form->add('user', EntityType::class, [
                'class' => Users::class,
                'mapped' => false,
                'choices' => [],
                'required' => false,
                'placeholder' => 'Selectionner une culture avant de choisir un utilisateur'
            ]);
         else 
            $form->add('user', EntityType::class, [
                'class' => Users::class,
                'choice_label' => function(Users $user) 
                    return $user->getIdentity();
                ,
                'query_builder' => function (UsersRepository $er) use ( $indexCultures ) 
                    return $er->createQueryBuilder('u')
                        ->leftJoin( Exploitation::class, 'e', 'WITH', 'u.id = e.users')
                        ->leftJoin(Ilots::class, 'i', 'WITH', 'e.id = i.exploitation')
                        ->leftJoin(Cultures::class, 'c', 'WITH', 'i.id = c.ilot')
                        ->leftJoin(IndexCultures::class, 'ic', 'WITH','c.name = ic.id')
                        ->andWhere('ic.id = :indexC')
                        ->setParameter('indexC', $indexCultures->getId());
                ,
                'mapped' => false,
                'expanded' => true,
                'multiple' => true
            ]);
        
    

    public function configureOptions(OptionsResolver $resolver)
    
        $resolver->setDefaults([
            'data_class' => BsvUsers::class,
            'translation_domain' => 'forms'
        ]);
    

我的控制器

/**
     * @Route("/admin/bsv/send/id", name="admin.bsv.send", methods="GET|POST")
     * @param Bsv $bsv
     * @param Request $request
     * @return Response
     * @throws \Exception
     */
    public function send(Bsv $bsv, Request $request): Response
    
        $bsvUsers = new BsvUsers();
        $form = $this->createForm(BsvSendType::class, $bsvUsers);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) 
            $data = $form->all();
            $customers = $data['user']->getData();
            $displayAt = $data['display_at']->getData();
            //-- Init
            $datetime = New \DateTime();
            //-- Update BSV info
            $bsv->setSendDate( $datetime );
            //-- Create relation
            foreach ($customers as $customer) 
                $relation = new BsvUsers();
                $this->em->persist($relation);
                $relation->setBsv($bsv);
                $relation->setCustomers($customer);
                $relation->setChecked(0);
                if ( $displayAt !== null ) 
                    $displayAt->setTime(8,00);
                    $relation->setDisplayAt($displayAt);
                 else 
                    $relation->setDisplayAt($datetime);
                
            
            $this->em->flush();
            $this->addFlash('success', 'BSV envoyé avec succès');
            return $this->redirectToRoute('admin.bsv.index');
        

        return $this->render('admin/bsv/send.html.twig', [
            'bsv' => $bsv,
            'form' => $form->createView()
        ]);
    

【问题讨论】:

【参考方案1】:

看起来您发送的表单没有 _token 字段,请查看您的 JS:

...

let data = 
data[$field.attr('name')] = $field.val()
$.post($form.attr('action'), data).then(...)

...

您的data 对象在发送到服务器之前仅包含#bsv_send_cultures 输入值,只需将#bsv_send__token 输入值添加到此对象,然后再从您的脚本发送。

【讨论】:

以上是关于CSRF 令牌无效。请尝试重新提交表单。 Symfony 4的主要内容,如果未能解决你的问题,请参考以下文章

Symfony3 中的错误“CSRF 令牌无效。请尝试重新提交表单”

直接访问登录表单时未设置会话 cookie,导致 CSRF 令牌无效

在测试中获取 CSRF 令牌

打开新的浏览器选项卡会使 Django 的 CSRF 令牌无效,从而阻止表单提交

Ajax GET 请求后表单提交失败(无效的 CSRF 令牌)。怎么修?

停止在身份验证时重新生成/使 CSRF 无效