如何根据symfony 2中的角色重定向到不同的url


How to redirect to different url based on roles in symfony 2

我在网站上有一个登录页面。我有4种不同类型的用户,我希望当他们登录时,他们会根据分配的角色转到不同的页面。

有办法吗?

解决此问题的一种方法是在security.interactive_login事件上使用事件侦听器。在这种情况下,我只需在该事件监听器中附加另一个监听器,这样它就会在响应中启动。这使得身份验证仍然可以进行,但一旦完成,仍然可以执行重定向。

<service id="sotb_core.listener.login" class="SOTB'CoreBundle'EventListener'SecurityListener" scope="request">
    <tag name="kernel.event_listener" event="security.interactive_login" method="onSecurityInteractiveLogin"/>
    <argument type="service" id="router"/>
    <argument type="service" id="security.context"/>
    <argument type="service" id="event_dispatcher"/>
</service>

班上。。。

class SecurityListener
{
    protected $router;
    protected $security;
    protected $dispatcher;
    public function __construct(Router $router, SecurityContext $security, EventDispatcher $dispatcher)
    {
        $this->router = $router;
        $this->security = $security;
        $this->dispatcher = $dispatcher;
    }
    public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
    {
        $this->dispatcher->addListener(KernelEvents::RESPONSE, array($this, 'onKernelResponse'));
    }
    public function onKernelResponse(FilterResponseEvent $event)
    {
        if ($this->security->isGranted('ROLE_TEAM')) {
            $response = new RedirectResponse($this->router->generate('team_homepage'));
        } elseif ($this->security->isGranted('ROLE_VENDOR')) {
            $response = new RedirectResponse($this->router->generate('vendor_homepage'));
        } else {
            $response = new RedirectResponse($this->router->generate('homepage'));
        }
        $event->setResponse($response);
    }
}

对于Symfony>=2.6,现在应该是:

<?php
namespace CommonBundle'Listener;
use Monolog'Logger;
use Symfony'Component'EventDispatcher'EventDispatcherInterface;
use Symfony'Component'HttpKernel'Event'FilterResponseEvent;
use Symfony'Component'HttpKernel'KernelEvents;
use Symfony'Component'Routing'Router;
use Symfony'Component'Security'Core'Authentication'Token'Storage'TokenStorage;
use Symfony'Component'Security'Http'Event'InteractiveLoginEvent;
class LoginListener
{
    /** @var Router */
    protected $router;
    /** @var TokenStorage */
    protected $token;
    /** @var EventDispatcherInterface */
    protected $dispatcher;
    /** @var Logger */
    protected $logger;
    /**
     * @param Router $router
     * @param TokenStorage $token
     * @param EventDispatcherInterface $dispatcher
     * @param Logger $logger
     */
    public function __construct(Router $router, TokenStorage $token, EventDispatcherInterface $dispatcher, Logger $logger)
    {
        $this->router       = $router;
        $this->token        = $token;
        $this->dispatcher   = $dispatcher;
        $this->logger       = $logger;
    }
    public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
    {
        $this->dispatcher->addListener(KernelEvents::RESPONSE, [$this, 'onKernelResponse']);
    }
    public function onKernelResponse(FilterResponseEvent $event)
    {
        $roles = $this->token->getToken()->getRoles();
        $rolesTab = array_map(function($role){
            return $role->getRole();
        }, $roles);
        $this->logger->info(var_export($rolesTab, true));
        if (in_array('ROLE_ADMIN', $rolesTab) || in_array('ROLE_SUPER_ADMIN', $rolesTab)) {
            $route = $this->router->generate('backend_homepage');
        } elseif (in_array('ROLE_CLIENT', $rolesTab)) {
            $route = $this->router->generate('frontend_homepage');
        } else {
            $route = $this->router->generate('portal_homepage');
        }
        $event->getResponse()->headers->set('Location', $route);
    }
}

和服务。yml

services:
common.listener.login:
    class: CommonBundle'Listener'LoginListener
    arguments: [@router, @security.token_storage, @event_dispatcher, @logger]
    scope: request
    tags:
        - { name: kernel.event_listener, event: security.interactive_login, method: onSecurityInteractiveLogin }

在Symfony 3.1 中测试

您还可以在security.yml文件中为所有用户成功登录后设置默认路径,如下所示:

[config/security.yml]

...
firewalls:
    # disables authentication for assets and the profiler, adapt it according to your needs
    dev:
        pattern: ^/(_(profiler|wdt)|css|images|js)/
        security: false
    main:
        pattern: /.*
        form_login:
            login_path: /login
            check_path: /login_check
            default_target_path: /login/redirect <<<<<<<<<<<<<<<<<<<<<<<<<
        logout:
            path: /logout
            target: /
        security: true
        anonymous: ~
...

然后default_target_path方法中基于用户角色进行简单重定向。非常直率。有人说,最简单的方法总是最好的方法。您决定:)

[SomeBundle/Controller/SomeController.php]

/**
 * Redirect users after login based on the granted ROLE
 * @Route("/login/redirect", name="_login_redirect")
 */
public function loginRedirectAction(Request $request)
{
    if (!$this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY'))
    {
        return $this->redirectToRoute('_login');
        // throw $this->createAccessDeniedException();
    }
    if($this->get('security.authorization_checker')->isGranted('ROLE_ADMIN'))
    {
        return $this->redirectToRoute('_admin_panel');
    }
    else if($this->get('security.authorization_checker')->isGranted('ROLE_USER'))
    {
        return $this->redirectToRoute('_user_panel');
    }
    else
    {
        return $this->redirectToRoute('_login');
    }
}

工作起来很有魅力,但请记住,如果您的ROLE_ADMIN也有ROLE_USER等特权,请始终向下检查最受限制的角色…

我使用了Mdrollette答案,但这个解决方案有一个很大的缺点,你可以完全覆盖symfony的原始响应,这样可以删除symfony在标头中设置的记住我的cookie。

我的解决方案是以这种方式更改OnKernelResponse:

public function onKernelResponse(FilterResponseEvent $event)
{
    if ($this->security->isGranted('ROLE_TEAM')) {
        $event->getResponse()->headers->set('Location', $this->router->generate('team_homepage'));    
    } elseif ($this->security->isGranted('ROLE_VENDOR')) {
        $event->getResponse()->headers->set('Location', $this->router->generate('vendor_homepage'));
    } else {
        $event->getResponse()->headers->set('Location', $this->router->generate('homepage'));
    }
}

这样你就能保持"记住我"的饼干完好无损。

如果您正在寻找比@MDrollette更简单的答案,您可以在登录成功页面的控制器中放入类似的重定向块。

为了测试,如果您想保留原始响应,也可以只复制头。Redirect对象上的clone方法只复制标头。

public function onKernelResponse(FilterResponseEvent $event)
{
    if ($this->security->isGranted('ROLE_TEAM')) {
        $response = new RedirectResponse($this->router->generate('team_homepage'));
    } elseif ($this->security->isGranted('ROLE_VENDOR')) {
        $response = new RedirectResponse($this->router->generate('vendor_homepage'));
    } else {
        $response = new RedirectResponse($this->router->generate('homepage'));
    }
    $response->headers = $response->headers + $event->getResponse()->headers;
    $event->setResponse($response);
}

我在登录表单验证器中使用了这个来根据角色重定向用户(symfony:4.26.8):

use Symfony'Component'Routing'Generator'UrlGeneratorInterface;
use Symfony'Component'Security'Core'Security;
private $urlGenerator;
/**
 * @var Security
 */
private $security;
public function __construct(UrlGeneratorInterface $urlGenerator ,Security $security)
{
    $this->urlGenerator = $urlGenerator;
    $this->security = $security;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
    {        
    // redirecting user by role : 
        $user = $this->security->getUser();
        $roles = $user->getRoles();
        $rolesTab = array_map(function($role){
            return $role;
        }, $roles);
        if (in_array('ROLE_ADMIN', $rolesTab) || in_array('ROLE_SUPER_ADMIN', $rolesTab)) {
            return new RedirectResponse($this->urlGenerator->generate('admin'));
        }
        else{
            return new RedirectResponse($this->urlGenerator->generate('home'));
    }
}