如何在监听器中进行重定向

时间:2014-12-26 16:09:34

标签: symfony

我正在尝试在监听器中进行重定向,但它不起作用。

我的听众:

<?php
// AuthenticationListener.php

namespace Acme\UserBundle\Listener;

use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Routing\Router;
use Symfony\Component\HttpFoundation\RedirectResponse;

class AuthenticationListener
{
    protected $router;

    public function __construct(Router $router)
    {
        $this->router = $router;
    }

    public function onAuthenticationFailure(AuthenticationFailureEvent $event)
    {
        if(preg_match("/m\/login/", $_SERVER['HTTP_REFERER'])){
            // var_dump('Hello'); die();
            return new RedirectResponse($this->router->generate('mobile_login'));
        }
    }

    public function onAuthenticationSuccess(InteractiveLoginEvent $event)
    {
        if(preg_match("/m\/login/", $_SERVER['HTTP_REFERER'])){
           return new RedirectResponse($this->router->generate('mobile_home'));
        }
    }
}

我需要这个,因为我有2种登录表单:一种用于PC,一种用于移动。因此,如果用户在登录/传递时出错,我需要显示正确的页面 / login / m / login

我的脚本有效。如果我取消注释var_dump('Hello');,我会看到“你好”。除了重定向之外,所有工作都很好。我不知道为什么。我没有错误,symfony只是不在乎并将我重定向到 / login 。显然它是“正常的”我并不是唯一一个解决这个问题的人,但我找不到解决方案。我刚读到某个地方,我需要一个Dispacher,但我不知道它是如何工作的。

如果你有一个很棒的解决方案。

修改

我的UserBundle / [...] / service.yml

# authentication failure event listener
acme_user.security.authentication_failure_event_listener:
    class: Acme\UserBundle\Listener\AuthenticationListener
    arguments: [@router]
    tags:
        - { name: kernel.event_listener, event: security.authentication.failure, method: onAuthenticationFailure }

# authentication success event listener
acme_user.security.interactive_login_listener:
    class: Acme\UserBundle\Listener\AuthenticationListener
    arguments: [@router]
    tags:
        - { name: kernel.event_listener, event: security.interactive_login, method: onAuthenticationSuccess }

我的UserBundle / [...] / routing.yml

mobile_login:
    pattern:  /m/login
    defaults: { _controller: AcmeUserBundle:Mobile:login }

编辑Abdallah Arffak

security:
    encoders:
        FOS\UserBundle\Model\UserInterface: sha512

    role_hierarchy:
        ROLE_MODO:        ROLE_USER
        ROLE_ADMIN:       ROLE_MODO
        ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_MODO, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    providers:
        fos_userbundle:
            id: fos_user.user_provider.username

    firewalls:
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false

        mobile:
            pattern:  ^/m
            anonymous: ~
            form_login:
                check_path: /m/login_check
                login_path: /m/login
                default_target_path: /m/admin
            logout:
                path:   /m/logout
                target: /

        main:
            pattern: ^/
            form_login:
                provider: fos_userbundle
                csrf_provider: form.csrf_provider
            logout:       true
            anonymous:    true
            switch_user:  true
            remember_me:
                key:      "%secret%"
                lifetime: 31536000
                path:     /
                domain:   ~

    always_authenticate_before_granting: true

    access_control:
        - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/m/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }

MobileController.php

<?php

namespace Acme\UserBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\SecurityContext;

class MobileController extends Controller
{

    public function loginAction(Request $request)
    {
        /** @var $session \Symfony\Component\HttpFoundation\Session\Session */
        $session = $request->getSession();

        // get the error if any (works with forward and redirect -- see below)
        if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
            $error = $request->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
        } elseif (null !== $session && $session->has(SecurityContext::AUTHENTICATION_ERROR)) {
            $error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
            $session->remove(SecurityContext::AUTHENTICATION_ERROR);
        } else {
            $error = '';
        }

        if ($error) {
            // TODO: this is a potential security risk (see http://trac.symfony-project.org/ticket/9523)
            $error = $error->getMessage();
        }
        // last username entered by the user
        $lastUsername = (null === $session) ? '' : $session->get(SecurityContext::LAST_USERNAME);

        $csrfToken = $this->container->has('form.csrf_provider')
            ? $this->container->get('form.csrf_provider')->generateCsrfToken('authenticate')
            : null;

        return $this->render('AcmeUserBundle:Mobile:login.html.twig', array(
            'last_username' => $lastUsername,
            'error'         => $error,
            'csrf_token' => $csrfToken,
        ));
    }

3 个答案:

答案 0 :(得分:2)

你不必通过“听众” 最好的方法是通过安全性

security.yml:

firewalls:

    dev:
        pattern:  ^/(_(profiler|wdt)|css|images|js)/
        security: false

    mobile:
        pattern:  ^/m
        anonymous: ~
        form_login:
            check_path: /m/login_check
            login_path: /m/login
            default_target_path: /m/admin
        logout:
            path:   /m/logout
            target: /

    main:
        pattern:  ^
        anonymous: ~
        form_login:
            check_path: /login_check
            login_path: /login
            default_target_path: /admin
        logout:
            path:   /logout
            target: /


access_control:
    - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/m/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }

routing.yml:

mobile_login:
    pattern:  /m/login
    defaults: { _controller: AcmeUserBundle:Mobile:login  }

mobile_check_special:
    pattern:  /m/login_check

mobile_logout_special:
    pattern:  /m/logout

答案 1 :(得分:1)

尝试这样的事情,未经测试:

security.yml:

...

firewalls:
    some_area:
        ...

        form_login:
            failure_handler: custom.security.authentication_failure_handler
            login_path: /login
            check_path: /login_check

        logout:
            path:   /logout
            target: /

...

AuthenticationFailureHandler.php

class AuthenticationFailureHandler extends DefaultAuthenticationFailureHandler
{

    private $router;

    public function __construct(HttpKernelInterface $httpKernel, HttpUtils $httpUtils, array $options, LoggerInterface $logger = null, RouterInterface $router)
    {
        parent::__construct($httpKernel, $httpUtils, $options, $logger);

        $this->router = $router;
    }

    ....

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {

        if(preg_match("/m\/login/", $_SERVER['HTTP_REFERER'])){
            return $this->httpUtils->createRedirectResponse($request, $this->router->generate('mobile_login'));
        }

        return parent::onAuthenticationFailure($request, $exception);
    }
}

服务配置:

....

<service id="custom.security.authentication_failure_handler" class="%custom.security.authentication_failure_handler.class%" public="false">
    <argument type="service" id="kernel" />
    <argument type="service" id="security.http_utils" />
    <argument type="collection" />
    <argument type="service" id="logger"  on-invalid="ignore" />
    <argument type="service" id="router" />
</service>
....

答案 2 :(得分:0)

尝试编写这样的服务声明:

services:
myservice:
    class: My\MyBundle\MyService
    arguments: [ @router ]
    scope: request

我认为你错过了范围属性。