dongqin1075 2014-04-09 00:12
浏览 51
已采纳

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

I am currently overloading SonataUser registration form so that I can have my own custom form when people create an account.

I have overloaded everything properly (the handler, the form type, the controller and the twig template). However, when I send the form, I only get the data back and a new user is not created. Therefore, I investigated and I found out that when I echoed this

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

I obtained an error saying the CSRF token is invalid. I am using Symfony 2.4.2 and sonata user 2.2.x-dev.

I will show you all the classes that I have overloaded. For now, they are mostly copy and pasted from their parents.

Here is my form handler

<?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();
    }
}

Here is my form type:

<?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';
    }
}

Here is my registration controller

<?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');
    }
}

Here are my services:

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]

Here is my sonata user configuration(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

My twig rendering:

{% 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 %}

I really don't know why I get the error:

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

because I have in my page {{form_rest(form)}} and the token field is present...

UPDATE I created a github repository so that my configuration can be pulled so you can see the problems for yourself. https://github.com/ima-tech/testSonataUser

  • 写回答

1条回答 默认 最新

  • dtypj3308 2014-04-14 12:56
    关注

    Alright, when I cloned your github repo, I was able to remove the CSRF error by tweaking a bit your services.

    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]
    

    You see, under sonata.user.registration.form.handler.default: in the arguments, you need to set @sonata.user.registration.form and @sonata.user.user_manager and not @fos_user.registration.form and @fos_user.user_manager

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 oracle集群安装出bug
  • ¥15 关于#python#的问题:自动化测试
  • ¥20 问题请教!vue项目关于Nginx配置nonce安全策略的问题
  • ¥15 教务系统账号被盗号如何追溯设备
  • ¥20 delta降尺度方法,未来数据怎么降尺度
  • ¥15 c# 使用NPOI快速将datatable数据导入excel中指定sheet,要求快速高效
  • ¥15 再不同版本的系统上,TCP传输速度不一致
  • ¥15 高德地图2.0 版本点聚合中Marker的位置无法实时更新,如何解决呢?
  • ¥15 DIFY API Endpoint 问题。
  • ¥20 sub地址DHCP问题