Bonjour à tous, j'ai un petit soucis j'aimerai crée deux LoginFormAuthenticator l'un pour les utilisateur front et l'autre pour les utilisateur en Back.
J'ai regarder la doc officiel de symfony mais cependant rien ne marche pour moi, quand je tape /profile je suis toujours rediriger vers le formulaire de login pour l'administrateur.
Voici mon code
Security.yml
security:
encoders:
App\Entity\Users:
algorithm: 'argon2i'
# maximum memory (in KiB) that may be used to compute the Argon2 hash
memory_cost: 1024
# number of times the Argon2 hash algorithm will be run
time_cost: 2
# number of threads to use for computing the Argon2 hash
threads: 2
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
app_user_provider:
entity:
class: App\Entity\Users
property: username
firewalls:
main:
anonymous: true
guard:
authenticators:
- App\Security\LoginFormAuthenticator
logout:
path: app_logout
remember_me:
secret: '%kernel.secret%'
lifetime: 604800
path: /
front:
anonymous: true
guard:
authenticators:
- App\Security\FrontLoginFormAuthenticator
logout:
path: app_logout
remember_me:
secret: '%kernel.secret%'
lifetime: 604800
path: /
# activate different ways to authenticate
# http_basic: true
# https://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate
# form_login: true
# https://symfony.com/doc/current/security/form_login_setup.html
role_hierarchy:
ROLE_ADMIN: ROLE_ADMIN
ROLE_USER: [ROLE_USER]
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/profile, roles: ROLE_USER }
LoginFormAuthenticator pour le BACK
<?php
namespace App\Security;
use App\Entity\Users;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
/**
* Class LoginFormAuthenticator
*
* @package App\Security
*
*/
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
use TargetPathTrait;
private $entityManager;
private $router;
private $csrfTokenManager;
private $passwordEncoder;
private $authorizationChecker;
/**
* LoginFormAuthenticator constructor.
*
* @param EntityManagerInterface $entityManager
* @param RouterInterface $router
* @param CsrfTokenManagerInterface $csrfTokenManager
* @param UserPasswordEncoderInterface $passwordEncoder
* @param AuthorizationCheckerInterface $authorizationChecker
*/
public function __construct(
EntityManagerInterface $entityManager,
RouterInterface $router,
CsrfTokenManagerInterface $csrfTokenManager,
UserPasswordEncoderInterface $passwordEncoder,
AuthorizationCheckerInterface $authorizationChecker
) {
$this->entityManager = $entityManager;
$this->router = $router;
$this->csrfTokenManager = $csrfTokenManager;
$this->passwordEncoder = $passwordEncoder;
$this->authorizationChecker = $authorizationChecker;
}
/**
* {@inheritdoc}
*/
public function supports(Request $request)
{
return 'app_login' === $request->attributes->get('_route')
&& $request->isMethod('POST');
}
/**
* {@inheritdoc}
*/
public function getCredentials(Request $request)
{
$credentials = [
'email' => strtolower($request->request->get('email')),
'password' => $request->request->get('password'),
'csrf_token' => $request->request->get('_csrf_token'),
];
$request->getSession()->set(
Security::LAST_USERNAME,
$credentials['email']
);
return $credentials;
}
/**
* {@inheritdoc}
*/
public function getUser($credentials, UserProviderInterface $userProvider)
{
$token = new CsrfToken('authenticate', $credentials['csrf_token']);
if (!$this->csrfTokenManager->isTokenValid($token)) {
throw new InvalidCsrfTokenException();
}
$user = $this->entityManager->getRepository(Users::class)->findOneBy(['email' => $credentials['email']]);
if (!$user) {
// fail authentication with a custom error
throw new CustomUserMessageAuthenticationException('error.invalid_user_account');
}
// user disabled
if (!$user->isEnabled()) {
throw new CustomUserMessageAuthenticationException('error.account_disabled');
}
return $user;
}
/**
* {@inheritdoc}
*/
public function checkCredentials($credentials, UserInterface $user)
{
return $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
}
/**
* {@inheritdoc}
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
return new RedirectResponse($targetPath);
}
$urlName = $this->authorizationChecker->isGranted(Users::ROLE_ADMIN) ? 'admin_index' : 'index_front';
// : ($this->authorizationChecker->isGranted(Users::ROLE_CHEF_RESEAUX) ? 'app_login' : '');
return new RedirectResponse($this->router->generate($urlName));
}
/**
* {@inheritdoc}
*/
protected function getLoginUrl()
{
return $this->router->generate('app_login');
}
}
LoginFormAuthenticator pour le Front
<?php
namespace App\Security;
use App\Entity\Users;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
/**
* Class LoginFormAuthenticator
*
* @package App\Security
*
*/
class FrontLoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
use TargetPathTrait;
private $entityManager;
private $router;
private $csrfTokenManager;
private $passwordEncoder;
private $authorizationChecker;
/**
* LoginFormAuthenticator constructor.
*
* @param EntityManagerInterface $entityManager
* @param RouterInterface $router
* @param CsrfTokenManagerInterface $csrfTokenManager
* @param UserPasswordEncoderInterface $passwordEncoder
* @param AuthorizationCheckerInterface $authorizationChecker
*/
public function __construct(
EntityManagerInterface $entityManager,
RouterInterface $router,
CsrfTokenManagerInterface $csrfTokenManager,
UserPasswordEncoderInterface $passwordEncoder,
AuthorizationCheckerInterface $authorizationChecker
) {
$this->entityManager = $entityManager;
$this->router = $router;
$this->csrfTokenManager = $csrfTokenManager;
$this->passwordEncoder = $passwordEncoder;
$this->authorizationChecker = $authorizationChecker;
}
/**
* {@inheritdoc}
*/
public function supports(Request $request)
{
return 'front_login' === $request->attributes->get('_route')
&& $request->isMethod('POST');
}
/**
* {@inheritdoc}
*/
public function getCredentials(Request $request)
{
$credentials = [
'email' => strtolower($request->request->get('email')),
'password' => $request->request->get('password'),
'csrf_tokens' => $request->request->get('_csrf_tokens'),
];
$request->getSession()->set(
Security::LAST_USERNAME,
$credentials['email']
);
return $credentials;
}
/**
* {@inheritdoc}
*/
public function getUser($credentials, UserProviderInterface $userProvider)
{
$token = new CsrfToken('authenticate', $credentials['csrf_tokens']);
if (!$this->csrfTokenManager->isTokenValid($token)) {
throw new InvalidCsrfTokenException();
}
$user = $this->entityManager->getRepository(Users::class)->findOneBy(['email' => $credentials['email']]);
if (!$user) {
// fail authentication with a custom error
throw new CustomUserMessageAuthenticationException('error.invalid_user_account');
}
// user disabled
if (!$user->isEnabled()) {
throw new CustomUserMessageAuthenticationException('error.account_disabled');
}
return $user;
}
/**
* {@inheritdoc}
*/
public function checkCredentials($credentials, UserInterface $user)
{
return $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
}
/**
* {@inheritdoc}
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
return new RedirectResponse($targetPath);
}
$urlName = $this->authorizationChecker->isGranted(Users::ROLE_USER) ? 'front_index' : 'index_front';
return new RedirectResponse($this->router->generate($urlName));
}
/**
* {@inheritdoc}
*/
protected function getLoginUrl()
{
return $this->router->generate('front_login');
}
}
Merci d'avance pour votre aide
Hello,
Quel est l'intérêt de créer deux guards?
Il ne faut pas mélanger le process d'authentification avec celui d'autorisation. Tu as un seul provider (ton entité User), donc tous tes users sont dans la même base si je ne m'abuse? Donc l'authentification s'effectue de la même manière non?
Ce sont les autorisations qui changent, suivant le rôle dont ton utilisateur dispose...
Tu pourrais donc avoir un seul guard, qui permet l'authentification, et ensuite tu te bases sur le rôle pour gérer les autorisations (ce que tu fais déja très bien dans la section access_control)....
Un autre guard serait utile, si par exemple, tu avais un second provider (oAuth, LDAP...) et donc un process d'authentification différent.
Au fait je penser à deux guard pour le fait que l'autre est à l'user back et l'autre à l'user front, puis comme tu le dis un seul suffit et j'ai enlever l'autre, maintenant c'est la vue qui pose problème car si je tape /admin je suis toujours rediriger vera /login au lieu de login-front
j'aimerai juste que les user front ai leur vue pour le login et la même chose pour les user back
Pourquoi 2 vues différentes pour la même chose (authentification)? Ce qui est intéressant c'est de rediriger ensuite par défaut sur une page différente (ou sur la page précédement demandée)...