Reputation: 31548
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 way?
Upvotes: 17
Views: 16700
Reputation: 11
I used this in the login Form authenticator to redirect user based on role (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'));
}
}
Upvotes: 0
Reputation: 18840
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...
Upvotes: 8
Reputation: 101
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 }
Upvotes: 10
Reputation: 5026
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);
}
Upvotes: 0
Reputation: 239
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.
Upvotes: 5
Reputation: 9262
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.
Upvotes: 2
Reputation: 6927
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);
}
}
Upvotes: 31