Bonjour à tous,
Je suis à la recherche d'idées pour résoudre notre problématique.
Nous avons actuellement plusieurs plateformes développées avec Symfony. L'idée serait de faire communiquer entre elles l'ensemble de ces plateformes.
Touts nos plateformes sont installées sur le même VPS et utilisent la même base de donnée.
Nous souhaiterions avoir une centralisation de le parti login et une interaction entre les projets.
Nous avons eu plusieurs idées :
Avez-vous déjà rencontré ce cas de figure ? Avez-vous d'autres idées à me proposer ?
J'espère avoir été claire dans mes explications et je vous remercie par avance pour votre aide.
Pour l'autentification pourquoi ne pas utiliser un des projets qui a déjà l'implémentation des logins mise en place comme maitre des logins ?
L'idée serait de mettre en place une nouvelle route genre API-Login en POST qui s'occupe de vérifier le login utilisateur et qui retourne par exemple un JSON ou autre comportant les données utilisateurs si le login est correct.
Qu'en au micro-service, ils utiliseraient le guard de symfony avec un nouveau authenticator qui étend AbstractFormLoginAuthenticator, dans la méthode getUser implémenter une requête POST avec Symfony http-client qui questionne la route API-Login du projet maître puis si c'est correcte de sauvegarder l'utilisateur dans la session.
C'est quelque chose que nous avons implémenté quelques fois lorsque des clients avaient plusieurs services inter-conectés. Voici un petit exemple rapide:
LoginAuthenticator
public function getUser($credentials, UserProviderInterface $userProvider)
{
$token = new CsrfToken('authenticate', $credentials['csrf_token']);
if (!$this->csrfTokenManager->isTokenValid($token)) {
throw new InvalidCsrfTokenException();
}
$content = null;
$client = HttpClient::create();
$response = $client->request('POST', 'https://blablab.com/api/user/login', [
'headers' => [
'Content-Type' => 'application/json',
],
'body' => json_encode(['username' => $credentials['username'], 'password' => $credentials['password']]),
]);
$statusCode = $response->getStatusCode();
if (Response::HTTP_FORBIDDEN === $statusCode) {
throw new CustomUserMessageAuthenticationException('Nom d\'utilisateur ou mot de passe incorrecte.');
}
if (200 !== $statusCode) {
throw new CustomUserMessageAuthenticationException('Une erreur est survenue, si le problème persiste veuillez contacter votre administrateur.');
}
$content = $response->toArray();
//save raw data to a custome key session
$this->session->set('user_raw_data', $content);
//load user via a custome provider
$user = $userProvider->loadUserByUsername($credentials['username']);
return $user;
}
Provider:
class FrontendLoginFormProvider implements UserProviderInterface
{
/** @var SessionInterface */
private $session;
public function __construct(SessionInterface $session)
{
$this->session = $session;
}
public function loadUserByUsername($username)
{
/** @var User $user */
$user = new User(
$username,
null,
['ROLE_USER']
);
return $user;
}
public function refreshUser(UserInterface $user)
{
/** @var User $user */
$user = new User(
$user->getUsername(),
null,
['ROLE_USER']
);
return $user;
}
public function supportsClass($class)
{
return true;
}
}
Pour avoir bossé sur une problématique similaire, je peux te donner quelques conseils
Premièrement : Si tu as besoin des entités des autres "projets", il vaut mieux oublier l'approche "Microservices". En effet, si une entité doit bouger, tu vas devoir t'amuser à copier/coller les entités dans tous les projets où elles sont nécessaires. L'approche microservice se fait dans une optique où un service est 100% indépendant d'un autre.
Tu as toujours l'option de faire un sous-projet git avec toutes les entités/repo/etc qui seront communs ou de créer un package composer privé avec juste ces données (on voulait passer par un npm entreprise, mais on "avait pas le budget")
Deuxièmement : Évitez de vous répétez dans votre code, c'est un calvaire de gérer les bugfix, nouvelles features et autre dans plusieurs projets (Pour avoir dû le faire dans 15 microservices différents, c'était ignoble). Passez par une partie commune (voire partie précédente)
Troisièmement :
Quatrièmement : Faites en sorte d'avoir un dispatcher en entrée qui redirige vers les autres projets. C'est chiant à mettre à jour quand il y a une nouveauté, mais ça devrait vous éviter d'avoir plusieurs liens pour accéder aux différentes applications
Bonjour Kareylo,
Je te remercie pour ton retour.
Si je comprends bien, tu me recommandes de garder mes projets séparés ?
Pour la partie login d'utiliser des outils type Auth0 ou Token puis un dispatcher vers les différents projets ?
Et pour la communication entre plateformes l'utilisation d'API ?
Le soucis, c'est que chaque projet à ses manières de foncitonner.
Me concernant, nous travaillions sur du microservice en API. Donc, la gestion "d'authentification" était gérée par un Token d'accès. J'ai parlé d'Auth0 car si je me souviens bien, une des solutions qu'ils permettent de mettre en place est d'avoir une authentification commune à plusieurs applis
Concernant le dispatcher, c'est assez compliqué à mettre en place mais largement réalisable (cependant, ça ralentit quand même pas mal le tout). Il y a des points positifs et négatifs aux différentes solutions, rien ne vous empêche de faire une étude de charge pour cette mise en place.
Pour la communications entre les plateformes, effectivement, l'API est le plus simple à mettre en place. Besoin de faire une action spécifique sur une autre appli ? Une petite requette HTTP et c'est fait.
Vous pouvez aussi vous tourner vers une approche socket interne aux applis (Bundle Mercure, RabbitMQ, ...) qui pourrait vous permettre de le faire (On était passé par cette approche en NodeJS avec RabbitMQ)
Après, le principale soucis vous concernant, c'est que vous avvez besoin des entités des autres projets. La mise à jour d'une entité entraine la mise à jour de plusieurs projets, d'où mon approche Sub-Git ou Packagist privé qui permettrait de le faire juste via un update.
en même temps, vue que la base de données est partagée. Pourquoi ne pas mettre pour chaque project un guard standard avec login et passwd, créer un bundle dédié en privé et l'isntaller sur chaque projet.
Edit : dans votre cas, je me demande si le mieux ce ne serait pas d'avoir une API Rest qui gère login + entités, du coup toute la logique, et que les autres micro-services font des appels à cette API ?
@Kareylo merci beaucoup pour ces précisions. Je comprends un peu mieux ton idée.
@Gulivert Je te remercie pour ton retour très détaillé. Après plusieurs retour je pense me diriger vers une architecture en micro-service et l'utilisation d'API REST pour la "communication" entre plateformes.
La partie authentification sera en effet soit géré par un projet indépendant.
Merci pour vos retours, j'y vois maintenant plus claire. Bonne journée!