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

后端 未结 6 1151
梦谈多话
梦谈多话 2020-12-02 17:37

I have one login page on site. I have 4 different tye of users and i want that when they login they go to different page based on their role assigned.

Is there any w

相关标签:
6条回答
  • 2020-12-02 18:25

    For Symfony >= 2.6 now would be:

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

    And services.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 }
    
    0 讨论(0)
  • 2020-12-02 18:26

    I used Mdrollette answer but this solution has a big drawback, you completely override the symfony original response and by doing this remove the remember me cookie that was set in the header by symfony.

    my solution was to change the OnKernelResponse this way :

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

    This way you remain the remember me cookie intact.

    0 讨论(0)
  • 2020-12-02 18:28

    One way to solve this is to use an event listener on the security.interactive_login event. In this case I simply attach another listener in that event listener so it will fire on the response. This lets the authentication still happen but still perform a redirect once complete.

    <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>
    

    And the class...

    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);
        }
    }
    
    0 讨论(0)
  • 2020-12-02 18:32

    If you are looking for a simpler answer than @MDrollette, you could put a similar redirect block into the controller of your login success page.

    0 讨论(0)
  • 2020-12-02 18:34

    For the sake of testing, if you're wanting to to preserve the original response you could also just copy the headers. The clone method on the Redirect object only copies the headers.

    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);
    }
    
    0 讨论(0)
  • 2020-12-02 18:36

    Tested in Symfony 3.1

    You could also set default path after user login successfully for all users in security.yml file like so:

    [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: ~
    ...
    

    and then in default_target_path method make simple redirection based on user role. Very straight forward. Some say that the easiest way is always the best way. You decide :)

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

    Works like a charm but keep in mind to always check for most restricted roles downwards in case your ROLE_ADMIN also has privileges of ROLE_USER and so on...

    0 讨论(0)
提交回复
热议问题