重定向到 next/route 操作,而不是用户 login/registration symfony 安全之后的最后一个操作

redirect to next/route action, not the last after user login/registration symfony security

我在视图中有这样的表单:

{{ form_start(form, {'action': path('process_route'), 'method': 'POST'}) }}
  {#  some other fields #}
  {{ form_row(form.placeOrder) }}
  {{ form_row(form.saveOrder) }}
{{ form_end(form) }}

如您所见,我有两个提交按钮,但这不是问题,我使用的是 symfony 方法:

if ($form->isValid()) {
    // ... do something

    // the save_and_add button was clicked
    if ($form->get('placeOrder')->isClicked()) {
        // probably redirect to the add page again
    }

    // redirect to the show page for the just submitted item
}

如您所见,我的表单有一个名为 'process_route' 特定 操作流程路由。 当我提交表单时,无论用户点击了提交按钮,我都会检查用户是否与 security.context 连接。

如果用户已连接,我将重定向到他点击两个提交按钮之一时调用的路由。

但用户未连接 我首先重定向到 user registration(如果尚未注册)或用户登录表单。

一旦用户注册或登录,我需要重定向到他点击两个提交按钮之一时首先询问的路线。

事实上,重定向效果很好,除了重定向是在 last requested 操作上执行的,e-g流程路由表单操作(此处'process_route'),我需要重定向到下一步,而不是最后一步。

在我的 security.yml 中,我有这个:

firewalls:
    main:
        pattern: ^/
            form_login:
                provider: custom
                csrf_provider: security.csrf.token_manager # Use form.csrf_provider instead for Symfony <2.4
                use_referer : true

如何通过单击两个提交按钮表单之一来管理用户要求的操作后的重定向,而不是最后一个操作?

在请求中手动设置referer

尝试直接从您的控制器执行此操作,例如:

if ($form->get('placeOrder')->isClicked()) {
    $request = $this->getRequest();
    $request->headers->set('referer', $this->generateUrl('expected_route'));
}

我怀疑下一个请求没有更新。
这种情况,找一个RequestListener,根据条件想办法设置好的referer

更新

对于登录,覆盖默认登录表单添加以下字段:

<input type="hidden" name="_target_path" value="{{ app.request.headers.get('referer') }}" />

在任何情况下都不要忘记从您的控制器手动设置引荐来源网址。

更新 2

由于 referer 未在登录表单中正确更新,我将尝试为您提供一个快速替代方案。

如果我没理解错的话,您是在控制器中手动呈现登录表单。

在呈现表单时,向视图传递一个附加参数:

return $this->render('UserBundle:Security:login.html.twig, array(
    'form' => $form->createView(),
    // ...
    '_target_path' => $this->generateUrl('expected_referer'), // Defined from your condition depending on button clicked
);

我找到了解决方案,但我想确保它是最佳实践。

请注意,此重定向必须仅在应用程序中的特定时刻执行,而不是在所有网站中执行。所以我决定创建一个服务监听器。

实际上,当会话具有我命名为êentity的特定实体对象时,必须执行重定向。

拳头,这是app/config/services.yml中服务的声明:

# apply the redirect response for connected user when and only where $isEntitySession !=null
    # to recover the current request in a service we need to set the scope argument, then call the symfony service container where $request is stored
    # just understand that it is not recommanded (bad structure and mad practice) to apply directly the request in service without service container
    my_bundle.login_listener:
        class: MySpace\MyBundle\EventListener\LoginListener
        tags:
            - { name: kernel.event_listener, event: security.interactive_login, method: onSecurityInteractiveLogin  }
        scope: request
        arguments: ["@router","@security.context","@event_dispatcher","@service_container"]

那么这是MySpace/MyBundle/EventListener/LoginListener.php中的LoginListener.php:

<?php

namespace MySpace\MyBundle\EventListener;

use Symfony\Component\Routing\Router;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\DependencyInjection\ContainerInterface;

class LoginListener
{
  protected $router;
  protected $security;
  protected $dispatcher;
  protected $container;

  public function __construct(Router $router, SecurityContext $security, EventDispatcherInterface $dispatcher, ContainerInterface $container)
  {
      $this->router = $router;
      $this->security = $security;
      $this->dispatcher = $dispatcher;
      $this->container = $container;
  }

  public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
  {
      $this->dispatcher->addListener(KernelEvents::RESPONSE, array($this, 'onKernelResponse'));
  }

  public function onKernelResponse(FilterResponseEvent $event)
  {
    $request = $this->container->get('request');
    $session = $this->container->get('session');

    $isEntitySession = $session->get('entity_form') != null;

    if($isEntitySession && $request->isMethod('POST') ) {
      $entity = $session->get('entity_form');
    }

    if(isset($entity)) {

      if ($entity->getState() == 'true') { //here the state is a bool, don't search anymore about this, it's just an entity object relation set to true or false. Following the state, I return a different view.

        $response = new RedirectResponse($this->router->generate('place_order_route'));

      } else {

        $response = new RedirectResponse($this->router->generate('save_order_route'));
      }

      $event->setResponse($response);
    }
  }
}

services.yml中的所有注释可以帮助您了解服务容器的应用。 symfony 文档告诉你不要在服务声明中直接使用 Request。不要犹豫,告诉我做的事情是否正确。

注意我用的是security.context,其实我是想通过recover user data来用的,但是作为这里的目的,没必要用,所以我们可以去掉这个argument__construct().

中的变量

为了找出单击了哪个按钮,我使用了实体 getState() 方法。确实,在这种状态之后,重定向是不一样的。