Я использую FOSUserBundle на своем веб-сайте Symfony2. Теперь я работаю над API, чтобы разрешить регистрацию по запросу REST api.
Я переопределил регистрационный контроллер FOSUserBundle:
ApiRegistrationController.php:
/** * @Route("/user/", defaults = { "_format" = "json" }, requirements = { "_method" = "POST" }) * */ public function registerAction(Request $request) { [...] $form = $formFactory->createForm(new ApiRegistrationFormType(), $user); [...] }
ApiRegistrationFormType.php:
/** * @param OptionsResolverInterface $resolver */ public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( 'data_class' => 'BenMarten\UserBundle\Entity\User', 'intention' => 'registration', 'csrf_protection' => false )); }
Я получаю ошибку о неправильном токере CSRF, поэтому я создал свой собственный RegisterFormType, чтобы отключить CSRF, но он должен быть вызван …
Как можно отключить токен CSRF только для вызовов api?
Поэтому после долгих разворачиваний я начал работать. Надеюсь, что это кому-то поможет.
Я использую FOSRestBundle.
Я создал свой собственный RestController в своем комплекте:
RestController.php
namespace BenMarten\UserBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use FOS\UserBundle\FOSUserEvents; use FOS\UserBundle\Event\FormEvent; use FOS\UserBundle\Event\GetResponseUserEvent; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use FOS\RestBundle\View\View; /** * Rest Controller */ class RestController extends Controller { /** * Create a new resource * * @param Request $request * @return View view instance * */ public function postUserAction(Request $request) { /** @var $formFactory \FOS\UserBundle\Form\Factory\FactoryInterface */ $formFactory = $this->container->get('fos_user.registration.form.factory'); /** @var $userManager \FOS\UserBundle\Model\UserManagerInterface */ $userManager = $this->container->get('fos_user.user_manager'); /** @var $dispatcher \Symfony\Component\EventDispatcher\EventDispatcherInterface */ $dispatcher = $this->container->get('event_dispatcher'); $user = $userManager->createUser(); $user->setEnabled(true); $event = new GetResponseUserEvent($user, $request); $dispatcher->dispatch(FOSUserEvents::REGISTRATION_INITIALIZE, $event); if (null !== $event->getResponse()) { return $event->getResponse(); } $form = $formFactory->createForm(); $form->setData($user); $jsonData = json_decode($request->getContent(), true); // "true" to get an associative array if ('POST' === $request->getMethod()) { $form->bind($jsonData); if ($form->isValid()) { $event = new FormEvent($form, $request); $dispatcher->dispatch(FOSUserEvents::REGISTRATION_SUCCESS, $event); $userManager->updateUser($user); $response = new Response("User created.", 201); return $response; } } $view = View::create($form, 400); return $this->get('fos_rest.view_handler')->handle($view); } }
RestRegistrationFormType.php
namespace BenMarten\UserBundle\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolverInterface; use Symfony\Component\DependencyInjection\Container; class RestRegistrationFormType extends AbstractType { protected $routeName; private $class; /** * @param string $class The User class name */ public function __construct(Container $container, $class) { $request = $container->get('request'); $this->routeName = $request->get('_route'); $this->class = $class; } public function buildForm(FormBuilderInterface $builder, array $options) { if ($this->routeName != "post_user") { $builder ->add('email', 'email', array('label' => 'form.email', 'translation_domain' => 'FOSUserBundle')) ->add('username', null, array('label' => 'form.username', 'translation_domain' => 'FOSUserBundle')) ->add('plainPassword', 'repeated', array( 'type' => 'password', 'options' => array('translation_domain' => 'FOSUserBundle'), 'first_options' => array('label' => 'form.password'), 'second_options' => array('label' => 'form.password_confirmation'), 'invalid_message' => 'fos_user.password.mismatch', )) ; } else { $builder ->add('email', 'email') ->add('username', null) ->add('plainPassword', 'password') ; } } public function setDefaultOptions(OptionsResolverInterface $resolver) { if ($this->routeName != "post_user") { $resolver->setDefaults(array( 'data_class' => $this->class, 'intention' => 'registration', )); } else { $resolver->setDefaults(array( 'data_class' => $this->class, 'intention' => 'registration', 'csrf_protection' => false )); } } public function getName() { return 'fos_user_rest_registration'; } }
Добавлена услуга Services.xml:
<service id="ben_marten.rest_registration.form.type" class="BenMarten\UserBundle\Form\RestRegistrationFormType"> <tag name="form.type" alias="fos_user_rest_registration" /> <argument type="service" id="service_container" /> <argument>BenMarten\UserBundle\Entity\User</argument> </service>
И в config.yml:
registration: form: type: fos_user_rest_registration
Поэтому в основном я говорю, что пользовательский комплект FOS использует мою собственную регистрационную форму, и в зависимости от маршрута я могу включить токен csrf или нет …
Предполагая, что вы используете как FOSRestBundle
и FOSOAuthServerBundle
, вы много работаете над тем, что уже реализовано … Я предлагаю … 2 дополнительных строки в вашем файле конфигурации 🙂
Приложение / config.yml
# we want to disable csrf roles for users with ROLE_API fos_rest: disable_csrf_role: ROLE_API # We want to use scope to assign this role when the user requests the token fos_oauth_server: options: supported_scopes: user api # https://goo.gl/iE9VoI
И вуаля!
Когда вы запрашиваете токен с областью действия api, ваш пользователь автоматически получает роль ROLE_API, а ограничение CSRF исчезло!
HTTP: // your_domain / OAuth / v2 / маркер client_id = YOUR_CLIENT_ID & client_secret = YOUR_CLIENT_SECREY & grant_type = пароль и имя пользователя = your_username и пароль = ваш_пароль и сфера = апи
Да, это в документе: