Symfony2 - 重载注册表单导致 CSRF 错误(添加 github repo)

Posted

技术标签:

【中文标题】Symfony2 - 重载注册表单导致 CSRF 错误(添加 github repo)【英文标题】:Symfony2 - Overload registration form causes CSRF errors (added github repo) 【发布时间】:2014-05-21 22:43:48 【问题描述】:

我目前正在重载 SonataUser 注册表单,以便在人们创建帐户时拥有自己的自定义表单。

我已经正确地重载了​​所有东西(处理程序、表单类型、控制器和树枝模板)。但是,当我发送表单时,我只取回数据并且没有创建新用户。因此,我进行了调查,发现当我回应这个时

var_dump($this->form->getErrors());

我收到一条错误消息,指出 CSRF 令牌无效。我正在使用Symfony 2.4.2sonata user 2.2.x-dev

我将向您展示我重载的所有类。目前,它们大多是从父母那里复制和粘贴的。

这是我的表单处理程序

<?php

/*
 * This file is part of the Sonata package.
 *
 * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */

namespace Application\Sonata\UserBundle\Form\Handler;

use Symfony\Component\Form\Form;
use Symfony\Component\HttpFoundation\Request;

use FOS\UserBundle\Model\UserInterface;
use FOS\UserBundle\Model\UserManagerInterface;
use FOS\UserBundle\Form\Handler\RegistrationFormHandler as BaseHandler;
use Symfony\Component\Form\FormInterface;


use FOS\UserBundle\Mailer\MailerInterface;
use FOS\UserBundle\Util\TokenGeneratorInterface;


/**
 *
 * This file is an adapted version of FOS User Bundle RegistrationFormHandler class
 *
 *    (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
 */
class RegistrationFormHandler extends BaseHandler

    protected $request;
    protected $userManager;
    protected $form;
    protected $mailer;
    protected $tokenGenerator;

    public function __construct(FormInterface $form, Request $request, UserManagerInterface $userManager, MailerInterface $mailer, TokenGeneratorInterface $tokenGenerator)
    

        $this->form = $form;
        $this->request = $request;
        $this->userManager = $userManager;
        $this->mailer = $mailer;
        $this->tokenGenerator = $tokenGenerator;
    

    /**
     * @param boolean $confirmation
     */
    public function process($confirmation = false)
    

        $user = $this->createUser();
        $this->form->setData($user);

        if ('POST' === $this->request->getMethod()) 

            $this->form->bind($this->request);
            if ($this->form->isValid()) 
                var_dump('working !!');
                $this->onSuccess($user, $confirmation);

                return true;
            
            var_dump($this->form->getErrors());
        

        return false;
    

    /**
     * @param boolean $confirmation
     */
    protected function onSuccess(UserInterface $user, $confirmation)
    
        if ($confirmation) 
            $user->setEnabled(false);
            if (null === $user->getConfirmationToken()) 
                $user->setConfirmationToken($this->tokenGenerator->generateToken());
            

            $this->mailer->sendConfirmationEmailMessage($user);
         else 
            $user->setEnabled(true);
        

        $this->userManager->updateUser($user);
    

    /**
     * @return UserInterface
     */
    protected function createUser()
    
        return $this->userManager->createUser();
    

这是我的表单类型:

<?php

/*
 * This file is part of the FOSUserBundle package.
 *
 * (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Application\Sonata\UserBundle\Form\Type;

use Entities\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Sonata\UserBundle\Model\UserInterface;

class RegistrationFormType extends AbstractType

    private $class;

    /**
     * @var array
     */
    protected $mergeOptions;

    /**
     * @param string $class        The User class name
     * @param array  $mergeOptions Add options to elements
     */
    public function __construct($class, array $mergeOptions = array())
    

        $this->class        = $class;
        $this->mergeOptions = $mergeOptions;
    

    public function buildForm(FormBuilderInterface $builder, array $options)
    
        $builder
            ->add('username', null, array_merge(array(
                'label' => 'form.username',
                'translation_domain' => 'SonataUserBundle',
            ), $this->mergeOptions))
            ->add('email', 'email', array_merge(array(
                'label' => 'form.email',
                'translation_domain' => 'SonataUserBundle',
            ), $this->mergeOptions))

            ->add('plainPassword', 'repeated', array_merge(array(
                'type' => 'password',
                'required' => true,
                'options' => array('translation_domain' => 'SonataUserBundle'),
                'first_options' => array_merge(array(
                    'label' => 'form.password',
                ), $this->mergeOptions),
                'second_options' => array_merge(array(
                    'label' => 'form.password_confirmation',
                ), $this->mergeOptions),
                'invalid_message' => 'fos_user.password.mismatch',
            ), $this->mergeOptions))

            ->add('lastName', null, array_merge(array(
                'label' => 'form.label_lastname',
                'translation_domain' => 'SonataUserBundle',
            ), $this->mergeOptions))
            ->add('firstName', null, array_merge(array(
                'label' => 'form.label_firstname',
                'translation_domain' => 'SonataUserBundle',
            ), $this->mergeOptions))
            ->add('date_of_birth', 'birthday', array_merge(array(
                'label' => 'form.label_date_of_birth',
                'translation_domain' => 'SonataUserBundle',
            ), $this->mergeOptions))
            ->add('gender', 'sonata_user_gender', array(
                'label'    => 'form.label_gender',
                'required' => true,
                'translation_domain' => 'SonataUserBundle',
                'choices' => array(
                    UserInterface::GENDER_FEMALE => 'gender_female',
                    UserInterface::GENDER_MALE   => 'gender_male',
                )
            ))
            ->add('phone', null, array_merge(array(
                'label' => 'form.label_phone',
                'translation_domain' => 'SonataUserBundle',
            ), $this->mergeOptions))

            ->add('address', null, array_merge(array(
                'label' => 'form.address',
                'translation_domain' => 'SonataUserBundle',
            ), $this->mergeOptions))

            ->add('city', null, array_merge(array(
                'label' => 'form.city',
                'translation_domain' => 'SonataUserBundle',
            ), $this->mergeOptions))

            ->add('state', 'choice', array_merge(array(
                'label' => 'form.state',
                'translation_domain' => 'SonataUserBundle',
                'multiple' => false,
                'expanded' => false
            ), $this->mergeOptions))

            ->add('country', 'choice', array_merge(array(
                'label' => 'form.country',
                'translation_domain' => 'SonataUserBundle',
                'multiple' => false,
                'expanded' => false
            ), $this->mergeOptions))

            ->add('postalCode', null, array_merge(array(
                'label' => 'form.postalCode',
                'translation_domain' => 'SonataUserBundle',
            ), $this->mergeOptions))

//            ->add('children', 'collection', array_merge(array(
//                'type' => new ChildFormType('Application\Sonata\UserBundle\Entity\User'),
//                'translation_domain' => 'SonataUserBundle',
//                'allow_add' => true,
//                'allow_delete' => true,
//                'by_reference' => false,
//            ), $this->mergeOptions))




        ;
    

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    
        $resolver->setDefaults(array(
            'data_class' => $this->class,
            'intention'  => 'registration',
        ));
    

    public function getName()
    
        return 'sonata_user_registration';
    

这是我的注册控制器

<?php
/*
 * This file is part of the Sonata package.
 *
 * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */


namespace Application\Sonata\UserBundle\Controller;

use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Exception\AccountStatusException;
use FOS\UserBundle\Model\UserInterface;

/**
 * Class SonataRegistrationController
 *
 * This class is inspired from the FOS RegistrationController
 *
 * @package Sonata\UserBundle\Controller
 *
 * @author Hugo Briand <briand@ekino.com>
 */
class RegistrationFOSUser1Controller extends ContainerAware

    public function registerAction()
    
        $user = $this->container->get('security.context')->getToken()->getUser();

        if ($user instanceof UserInterface && 'POST' === $this->container->get('request')->getMethod()) 
            $this->container->get('session')->getFlashBag()->set('sonata_user_error', 'sonata_user_already_authenticated');
            $url = $this->container->get('router')->generate('sonata_user_profile_show');

            return new RedirectResponse($url);
        

        $form = $this->container->get('sonata.user.registration.form');
        $formHandler = $this->container->get('sonata.user.registration.form.handler');
        $confirmationEnabled = $this->container->getParameter('fos_user.registration.confirmation.enabled');

        $process = $formHandler->process($confirmationEnabled);
        var_dump(0);
        if ($process) 
            var_dump(1);
            exit();
            $user = $form->getData();

            $authUser = false;
            if ($confirmationEnabled) 
                $this->container->get('session')->set('fos_user_send_confirmation_email/email', $user->getEmail());
                $route = 'fos_user_registration_check_email';
             else 
                $authUser = true;
                $route = $this->container->get('session')->get('sonata_basket_delivery_redirect', 'sonata_user_profile_show');
                $this->container->get('session')->remove('sonata_basket_delivery_redirect');
            

            $this->setFlash('fos_user_success', 'registration.flash.user_created');
            $url = $this->container->get('session')->get('sonata_user_redirect_url');

            if (null === $url || "" === $url) 
                $url = $this->container->get('router')->generate($route);
            

            $response = new RedirectResponse($url);

            if ($authUser) 
                $this->authenticateUser($user, $response);
            

            return $response;
        

        $this->container->get('session')->set('sonata_user_redirect_url', $this->container->get('request')->headers->get('referer'));

        return $this->container->get('templating')->renderResponse('FOSUserBundle:Registration:register.html.'.$this->getEngine(), array(
            'form' => $form->createView(),
        ));
    

    /**
     * Tell the user to check his email provider
     */
    public function checkEmailAction()
    
        $email = $this->container->get('session')->get('fos_user_send_confirmation_email/email');
        $this->container->get('session')->remove('fos_user_send_confirmation_email/email');
        $user = $this->container->get('fos_user.user_manager')->findUserByEmail($email);

        if (null === $user) 
            throw new NotFoundHttpException(sprintf('The user with email "%s" does not exist', $email));
        

        return $this->container->get('templating')->renderResponse('FOSUserBundle:Registration:checkEmail.html.'.$this->getEngine(), array(
            'user' => $user,
        ));
    

    /**
     * Receive the confirmation token from user email provider, login the user
     */
    public function confirmAction($token)
    
        $user = $this->container->get('fos_user.user_manager')->findUserByConfirmationToken($token);

        if (null === $user) 
            throw new NotFoundHttpException(sprintf('The user with confirmation token "%s" does not exist', $token));
        

        $user->setConfirmationToken(null);
        $user->setEnabled(true);
        $user->setLastLogin(new \DateTime());

        $this->container->get('fos_user.user_manager')->updateUser($user);
        if ($redirectRoute = $this->container->getParameter('sonata.user.register.confirm.redirect_route')) 
            $response = new RedirectResponse($this->container->get('router')->generate($redirectRoute, $this->container->getParameter('sonata.user.register.confirm.redirect_route_params')));
         else 
            $response = new RedirectResponse($this->container->get('router')->generate('fos_user_registration_confirmed'));
        

        $this->authenticateUser($user, $response);

        return $response;
    

    /**
     * Tell the user his account is now confirmed
     */
    public function confirmedAction()
    
        $user = $this->container->get('security.context')->getToken()->getUser();
        if (!is_object($user) || !$user instanceof UserInterface) 
            throw new AccessDeniedException('This user does not have access to this section.');
        

        return $this->container->get('templating')->renderResponse('FOSUserBundle:Registration:confirmed.html.'.$this->getEngine(), array(
            'user' => $user,
        ));
    

    /**
     * Authenticate a user with Symfony Security
     *
     * @param \FOS\UserBundle\Model\UserInterface        $user
     * @param \Symfony\Component\HttpFoundation\Response $response
     */
    protected function authenticateUser(UserInterface $user, Response $response)
    
        try 
            $this->container->get('fos_user.security.login_manager')->loginUser(
                $this->container->getParameter('fos_user.firewall_name'),
                $user,
                $response);
         catch (AccountStatusException $ex) 
            // We simply do not authenticate users which do not pass the user
            // checker (not enabled, expired, etc.).
        
    

    /**
     * @param string $action
     * @param string $value
     */
    protected function setFlash($action, $value)
    
        $this->container->get('session')->getFlashBag()->set($action, $value);
    

    protected function getEngine()
    
        return $this->container->getParameter('fos_user.template.engine');
    

这是我的服务:

sonata.user.registration.form.type:
    class: Application\Sonata\UserBundle\Form\Type\RegistrationFormType
    arguments: [ "%fos_user.model.user.class%"]
    tags:
        -  name: form.type, alias: sonata_user_registration 

sonata.child.registration.form.type:
    class: Application\Sonata\UserBundle\Form\Type\ChildFormType
    arguments: [ "%fos_user.model.user.class%"]
    tags:
        -  name: form.type, alias: sonata_child_registration 

sonata.user.registration.form.handler.default:
    class: Application\Sonata\UserBundle\Form\Handler\RegistrationFormHandler
    scope: request
    public: false
    arguments: [@fos_user.registration.form, @request, @fos_user.user_manager, @fos_user.mailer, @fos_user.util.token_generator]

这是我的奏鸣曲用户配置(app/config/config.yml)

sonata_user:
    security_acl:           false

    manager_type: orm # Can be orm for mongodb

    table:
        user_group: "my_custom_user_group_association_table_name"

    impersonating:
        route:                page_slug
        parameters:            path: / 

    class:                  # Entity Classes
        user:               Application\Sonata\UserBundle\Entity\User
        group:              Application\Sonata\UserBundle\Entity\Group

    admin:                  # Admin Classes
        user:
            class:          Sonata\UserBundle\Admin\Entity\UserAdmin
            controller:     SonataAdminBundle:CRUD
            translation:    SonataUserBundle

        group:
            class:          Sonata\UserBundle\Admin\Entity\GroupAdmin
            controller:     SonataAdminBundle:CRUD
            translation:    SonataUserBundle

    profile:  # Profile Form (firstname, lastname, etc ...)
        form:
            type:               sonata_user_profile
            handler:            sonata.user.profile.form.handler.default
            name:               sonata_user_profile_form
            validation_groups:  [Profile]

        register:
            # You may customize the registration forms over here
            form:
                type:                 sonata_user_registration
                handler:              sonata.user.registration.form.handler.default
                name:                 sonata_user_registration_form
                validation_groups:

                    # Defaults:
                    - Registration
                    - Default

我的树枝渲染:

% block fos_user_content %
    <br>
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <div class="well">
                <div class="panel-heading">
                    <h3> 'title_user_registration'|trans(, 'SonataUserBundle') </h3>
                </div>
                <div class="panel-body">
                    <form ng-app="userRegistrationApp" action=" path('fos_user_registration_register') "  form_enctype(form)  method="POST" class="fos_user_registration_register form-horizontal">

                        <h4> 'General'|trans(, 'SonataUserBundle') </h4>
                        <hr>


                        <div class="form-group">
                             form_label(form.username, null, 'label_attr': 'class': 'col-xs-4 control-label') 
                            <div class="col-xs-8">
                                 form_widget(form.username, 'attr': 'class': 'form-control') 
                            </div>
                        </div>

                        <div class="form-group">
                             form_label(form.email, null, 'label_attr': 'class': 'col-xs-4 control-label') 
                            <div class="col-xs-8">
                                 form_widget(form.email, 'attr': 'class': 'form-control') 
                            </div>
                        </div>

                        <br>
                        <h4> 'form.label_plain_password'|trans(, 'SonataUserBundle') </h4>
                        <hr>

                        <div class="form-group">
                             form_label(form.plainPassword.first, null, 'label_attr': 'class': 'col-xs-4 control-label') 
                            <div class="col-xs-8">
                                 form_widget(form.plainPassword.first, 'attr': 'class': 'form-control') 
                            </div>
                        </div>

                        <div class="form-group">
                             form_label(form.plainPassword.second, null, 'label_attr': 'class': 'col-xs-4 control-label') 
                            <div class="col-xs-8">
                                 form_widget(form.plainPassword.second, 'attr': 'class': 'form-control') 
                            </div>
                        </div>

                        <br>
                        <h4> 'Profile'|trans(, 'SonataUserBundle') </h4>
                        <hr>

                        <div class="form-group">
                             form_label(form.lastName, null, 'label_attr': 'class': 'col-xs-4 control-label') 
                            <div class="col-xs-8">
                                 form_widget(form.lastName, 'attr': 'class': 'form-control') 
                            </div>
                        </div>

                        <div class="form-group">
                             form_label(form.firstName, null, 'label_attr': 'class': 'col-xs-4 control-label') 
                            <div class="col-xs-8">
                                 form_widget(form.firstName, 'attr': 'class': 'form-control') 
                            </div>
                        </div>

                        <div class="form-group">
                             form_label(form.date_of_birth, null, 'label_attr': 'class': 'col-xs-4 control-label') 
                            <div class="col-xs-8">
                                 form_widget(form.date_of_birth, 'attr': 'class': '' ) 
                            </div>
                        </div>

                        <div class="form-group">
                             form_label(form.gender, null, 'label_attr': 'class': 'col-xs-4 control-label') 
                            <div class="col-xs-8">
                                 form_widget(form.gender, 'attr': 'class': '') 
                            </div>
                        </div>

                        <div class="form-group">
                             form_label(form.phone, null, 'label_attr': 'class': 'col-xs-4 control-label') 
                            <div class="col-xs-8">
                                 form_widget(form.phone, 'attr': 'class': 'form-control bfh-phone', 'data-country':'sonata_user_registration_form_country') 
                            </div>
                        </div>

                        <div class="form-group">
                             form_label(form.address, null, 'label_attr': 'class': 'col-xs-4 control-label') 
                            <div class="col-xs-8">
                                 form_widget(form.address, 'attr': 'class': 'form-control') 
                            </div>
                        </div>

                        <div class="form-group">
                             form_label(form.city, null, 'label_attr': 'class': 'col-xs-4 control-label') 
                            <div class="col-xs-8">
                                 form_widget(form.city, 'attr': 'class': 'form-control') 
                            </div>
                        </div>

                        <div class="form-group">
                             form_label(form.country, null, 'label_attr': 'class': 'col-xs-4 control-label') 
                            <div class="col-xs-8">
                                 form_widget(form.country, 'attr': 'class': 'form-control bfh-countries', ' data-country':'US') 
                            </div>
                        </div>

                        <div class="form-group">
                             form_label(form.state, null, 'label_attr': 'class': 'col-xs-4 control-label') 
                            <div class="col-xs-8">
                                 form_widget(form.state, 'attr': 'class': 'form-control bfh-states', 'data-country':'sonata_user_registration_form_country') 
                            </div>
                        </div>

                        <div class="form-group">
                             form_label(form.postalCode, null, 'label_attr': 'class': 'col-xs-4 control-label') 
                            <div class="col-xs-8">
                                 form_widget(form.postalCode, 'attr': 'class': 'form-control') 
                            </div>
                        </div>

                        <br>

                         form_rest(form) 


                        #<a href="#Children" class="btn btn-link" ng-click="userRegistrationService.addEmptyChild()"><span class="glyphicon glyphicon-plus-sign"></span>  'AddAChildren'|trans(, 'SonataUserBundle') </a>#


                        <div class="form-actions">
                            <button type="submit" class="btn btn-success pull-right"> 'registration.submit'|trans(, 'FOSUserBundle') </button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
% endblock fos_user_content %

我真的不知道为什么会出现错误:

array (size=1)
  0 => 
    object(Symfony\Component\Form\FormError)[1125]
      private 'message' => string 'Le jeton CSRF est invalide. Veuillez renvoyer le formulaire.' (length=60)
      protected 'messageTemplate' => string 'Le jeton CSRF est invalide. Veuillez renvoyer le formulaire.' (length=60)
      protected 'messageParameters' => 
        array (size=0)
          empty
      protected 'messagePluralization' => null

因为我的页面中有form_rest(form) 并且存在令牌字段...

更新我创建了一个 github 存储库,以便可以提取我的配置,以便您自己查看问题。 https://github.com/ima-tech/testSonataUser

【问题讨论】:

你能不能通过集成新的 symfony 和 sonata 设置来重现这个并将你的代码上传到 github。为我们解决您的问题会更容易 好吧,我终于有时间重做它,我有一个 github repo,这里是:github.com/ima-tech/testSonataUser 不幸的是问题仍然出现...... @user1191081 似乎即使在全新安装后问题仍然存在...... 【参考方案1】:

好的,当我克隆你的 github 存储库时,我能够通过稍微调整你的服务来消除 CSRF 错误。

parameters:
#    osc_default.example.class: OSC\DefaultBundle\Example

services:
#    osc_default.example:
#        class: %osc_default.example.class%
#        arguments: [@service_id, "plain_value", %parameter%]

    sonata.user.registration.form.type:
        class: Application\Sonata\UserBundle\Form\Type\RegistrationFormType
        arguments: [ "%fos_user.model.user.class%"]
        tags:
            -  name: form.type, alias: sonata_user_registration 

    sonata.child.registration.form.type:
        class: Application\Sonata\UserBundle\Form\Type\ChildFormType
        arguments: [ "%fos_user.model.user.class%"]
        tags:
            -  name: form.type, alias: sonata_child_registration 

    sonata.user.registration.form.handler.default:
        class: Application\Sonata\UserBundle\Form\Handler\RegistrationFormHandler
        scope: request
        public: false
        arguments: [@sonata.user.registration.form, @request, @sonata.user.user_manager, @fos_user.mailer, @fos_user.util.token_generator]

你看,在参数的sonata.user.registration.form.handler.default:下,你需要设置@sonata.user.registration.form@sonata.user.user_manager而不是@fos_user.registration.form@fos_user.user_manager

【讨论】:

以上是关于Symfony2 - 重载注册表单导致 CSRF 错误(添加 github repo)的主要内容,如果未能解决你的问题,请参考以下文章

CSRF 和 RESTful API (symfony2, php)

symfony2 CSRF 无效

在 ajax 提交时禁用 symfony 2 csrf 令牌保护

Symfony 2 在使用没有类的表单时添加 CSRF 令牌

Symfony2 防止多个表单提交

Symfony2 - 扩展或替换 SonataUserBundle 注册表单