Maison >développement back-end >tutoriel php >Authentification utilisateur dans Symfony2 avec userApp.io
userApp.io est un outil de gestion des utilisateurs pratique et une API. Il fournit une interface Web pour traiter les comptes d'utilisateurs (et les nombreuses fonctionnalités que cela implique) et une API pour les accrocher à votre propre application Web. Le but de ce service est de faciliter la gestion de l'authentification des utilisateurs plus facile et plus sûr en n'ayant pas à se soucier de cela sur votre propre serveur.
Il a des SDK et divers emballages pour de nombreux langages et cadres de programmation et le prix est abordable. Oui, cela vient avec un prix, mais vous pouvez commencer librement avec beaucoup de choses avec lesquelles jouer. Je recommande de consulter leur page de fonctionnalités pour obtenir plus d'informations. En outre, il est très facile de créer un compte et d'expérimenter avec la création d'utilisateurs, l'ajout de propriétés à leurs profils, etc., donc je vous recommande également de vérifier cela également si vous ne l'avez pas déjà fait.
Dans cet article, nous allons voir comment nous pouvons implémenter un mécanisme d'authentification Symfony2 qui exploite userApp.io. Le code que nous écrivons peut également être trouvé dans cette petite bibliothèque que j'ai créée (actuellement dans Dev) que vous pouvez essayer. Pour l'installer dans votre application Symfony, suivez simplement les instructions sur GitHub.
Afin de communiquer avec le service userApp.io, nous utiliserons sa bibliothèque PHP. Assurez-vous d'avoir besoin de cela dans le fichier composer.json de votre application Symfony comme indiqué sur leur page GitHub.
Pour authentifier les utilisateurs userApp.io avec notre application Symfony, nous créerons quelques classes:
Une fois que nous aurons créé ces classes, nous en déclarerons certains comme services et les utiliserons dans le système de sécurité Symfony.
Tout d'abord, nous créerons la classe la plus importante, l'authentificateur de formulaire (à l'intérieur d'une sécurité / dossier de notre meilleure pratique nommée AppBundle). Voici le code, je l'expliquerai après:
<span><span><?php </span></span><span> </span><span><span>/** </span></span><span><span> * <span>@file AppBundle\Security\UserAppAuthenticator.php </span></span></span><span><span> */ </span></span><span> </span><span><span>namespace AppBundle<span>\Security</span>; </span></span><span> </span><span><span>use Symfony<span>\Component\HttpFoundation\Request</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\Authentication\SimpleFormAuthenticatorInterface</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\Authentication\Token\TokenInterface</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\Exception\AuthenticationException</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\User\UserProviderInterface</span>; </span></span><span><span>use UserApp<span>\API</span> as UserApp; </span></span><span><span>use UserApp<span>\Exceptions\ServiceException</span>; </span></span><span> </span><span><span>class UserAppAuthenticator implements SimpleFormAuthenticatorInterface </span></span><span><span>{ </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>@var UserApp </span></span></span><span><span> */ </span></span><span> <span>private $userAppClient; </span></span><span> </span><span> <span>public function __construct(UserApp $userAppClient) { </span></span><span> <span>$this->userAppClient = $userAppClient; </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>{@inheritdoc} </span></span></span><span><span> */ </span></span><span> <span>public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey) </span></span><span> <span>{ </span></span><span> </span><span> <span>try { </span></span><span> <span>$login = $this->userAppClient->user->login(array( </span></span><span> <span>"login" => $token->getUsername(), </span></span><span> <span>"password" => $token->getCredentials(), </span></span><span> <span>) </span></span><span> <span>); </span></span><span> </span><span> <span>// Load user from provider based on id </span></span><span> <span>$user = $userProvider->loadUserByLoginInfo($login); </span></span><span> <span>} catch(ServiceException $exception) { </span></span><span> <span>if ($exception->getErrorCode() == 'INVALID_ARGUMENT_LOGIN' || $exception->getErrorCode() == 'INVALID_ARGUMENT_PASSWORD') { </span></span><span> <span>throw new AuthenticationException('Invalid username or password'); </span></span><span> <span>} </span></span><span> <span>if ($exception->getErrorCode() == 'INVALID_ARGUMENT_APP_ID') { </span></span><span> <span>throw new AuthenticationException('Invalid app ID'); </span></span><span> <span>} </span></span><span> <span>} </span></span><span> <span>return new UserAppToken( </span></span><span> <span>$user, </span></span><span> <span>$user->getToken(), </span></span><span> <span>$providerKey, </span></span><span> <span>$user->getRoles() </span></span><span> <span>); </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>{@inheritdoc} </span></span></span><span><span> */ </span></span><span> <span>public function supportsToken(TokenInterface $token, $providerKey) </span></span><span> <span>{ </span></span><span> <span>return $token instanceof UserAppToken </span></span><span> <span>&& $token->getProviderKey() === $providerKey; </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>{@inheritdoc} </span></span></span><span><span> */ </span></span><span> <span>public function createToken(Request $request, $username, $password, $providerKey) </span></span><span> <span>{ </span></span><span> <span>return new UserAppToken($username, $password, $providerKey); </span></span><span> <span>} </span></span><span><span>}</span></span>
Comme vous pouvez le voir, nous mettons en œuvre le SimpleFormAuthenticatorInterface et avons par conséquent 3 méthodes et un constructeur. Ce dernier prend une dépendance en tant que client userApp.io instancié (passé à l'aide du conteneur de service, mais plus à ce sujet en une minute).
Cette classe est utilisée par Symfony lorsqu'un utilisateur essaie de se connecter et de s'authentifier avec l'application. La première chose qui se produit est que CreateToken () est appelé. Cette méthode doit renvoyer un jeton d'authentification qui combine le nom d'utilisateur et le mot de passe soumis. Dans notre cas, ce sera une instance de la classe UserAppToken que nous définirons dans un instant.
Ensuite, la méthode supportToken () est appelée pour vérifier si cette classe prend en charge le jeton renvoyé par CreateToken (). Ici, nous nous assurons simplement de revenir vrai pour notre type de jeton.
Enfin, authenticateToken () est appelé et tente de vérifier si les informations d'identification dans le jeton sont valides. Ici, et en utilisant la bibliothèque UserApp.io PHP, nous essayons de vous connecter ou de lancer une exception d'authentification Symfony si cela échoue. Si l'authentification réussit cependant, le fournisseur d'utilisateurs responsable est utilisé pour construire notre objet utilisateur, avant de créer et de renvoyer un autre objet de jeton en fonction de ce dernier.
Nous rédigerons notre fournisseur d'utilisateurs juste après avoir rapidement créé la classe UserAppToken simple.
<span><span><?php </span></span><span> </span><span><span>/** </span></span><span><span> * <span>@file AppBundle\Security\UserAppToken.php </span></span></span><span><span> */ </span></span><span> </span><span><span>namespace AppBundle<span>\Security</span>; </span></span><span> </span><span><span>use Symfony<span>\Component\Security\Core\Authentication\Token\UsernamePasswordToken</span>; </span></span><span> </span><span><span>class UserAppToken extends UsernamePasswordToken { </span></span><span> </span><span><span>}</span></span>
Comme vous pouvez le voir, ce n'est qu'une extension de la classe UsernamepasswordToken pour le nom de la dénomination étant plus précise (puisque nous stockons un jeton au lieu d'un mot de passe).
Ensuite, voyons comment l'authentificateur fonctionne avec le fournisseur d'utilisateurs, il est donc temps de créer également ce dernier:
<span><span><?php </span></span><span> </span><span><span>/** </span></span><span><span> * <span>@file AppBundle\Security\UserAppProvider.php </span></span></span><span><span> */ </span></span><span> </span><span><span>namespace AppBundle<span>\Security</span>; </span></span><span> </span><span><span>use Symfony<span>\Component\Security\Core\Exception\AuthenticationException</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\Exception\UsernameNotFoundException</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\User\UserProviderInterface</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\User\UserInterface</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\Exception\UnsupportedUserException</span>; </span></span><span><span>use UserApp<span>\API</span> as UserApp; </span></span><span><span>use UserApp<span>\Exceptions\ServiceException</span>; </span></span><span><span>use AppBundle<span>\Security\Exception\NoUserRoleException</span>; </span></span><span><span>use AppBundle<span>\Security\UserAppUser</span>; </span></span><span> </span><span><span>class UserAppProvider implements UserProviderInterface </span></span><span><span>{ </span></span><span> <span>/** </span></span><span><span> * <span>@var UserApp </span></span></span><span><span> */ </span></span><span> <span>private $userAppClient; </span></span><span> </span><span> <span>public function __construct(UserApp $userAppClient) { </span></span><span> <span>$this->userAppClient = $userAppClient; </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>{@inheritdoc} </span></span></span><span><span> */ </span></span><span> <span>public function loadUserByUsername($username) </span></span><span> <span>{ </span></span><span> <span>// Empty for now </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>{@inheritdoc} </span></span></span><span><span> */ </span></span><span> <span>public function refreshUser(UserInterface $user) </span></span><span> <span>{ </span></span><span> <span>if (!$user instanceof UserAppUser) { </span></span><span> <span>throw new UnsupportedUserException( </span></span><span> <span>sprintf('Instances of "%s" are not supported.', get_class($user)) </span></span><span> <span>); </span></span><span> <span>} </span></span><span> </span><span> <span>try { </span></span><span> <span>$api = $this->userAppClient; </span></span><span> <span>$api->setOption('token', $user->getToken()); </span></span><span> <span>$api->token->heartbeat(); </span></span><span> <span>$user->unlock(); </span></span><span> <span>} </span></span><span> <span>catch (ServiceException $exception) { </span></span><span> <span>if ($exception->getErrorCode() == 'INVALID_CREDENTIALS') { </span></span><span> <span>throw new AuthenticationException('Invalid credentials'); </span></span><span> <span>} </span></span><span> <span>if ($exception->getErrorCode() == 'AUTHORIZATION_USER_LOCKED') { </span></span><span> <span>$user->lock(); </span></span><span> <span>} </span></span><span> <span>} </span></span><span> </span><span> <span>return $user; </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>{@inheritdoc} </span></span></span><span><span> */ </span></span><span> <span>public function supportsClass($class) </span></span><span> <span>{ </span></span><span> <span>return $class === 'AppBundle\Security\UserAppUser'; </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * </span></span><span><span> * Loads a user from UserApp.io based on a successful login response. </span></span><span><span> * </span></span><span><span> * <span>@param $login </span></span></span><span><span> * <span>@return UserAppUser </span></span></span><span><span> * <span>@throws NoUserRoleException </span></span></span><span><span> */ </span></span><span> <span>public function loadUserByLoginInfo($login) { </span></span><span> </span><span> <span>try { </span></span><span> <span>$api = $this->userAppClient; </span></span><span> <span>$api->setOption('token', $login->token); </span></span><span> <span>$users = $api->user->get(); </span></span><span> <span>} catch(ServiceException $exception) { </span></span><span> <span>if ($exception->getErrorCode() == 'INVALID_ARGUMENT_USER_ID') { </span></span><span> <span>throw new UsernameNotFoundException(sprintf('User with the id "%s" not found.', $login->user_id)); </span></span><span> <span>} </span></span><span> <span>} </span></span><span> </span><span> <span>if (!empty($users)) { </span></span><span> <span>return $this->userFromUserApp($users[0], $login->token); </span></span><span> <span>} </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * Creates a UserAppUser from a user response from UserApp.io </span></span><span><span> * </span></span><span><span> * <span>@param $user </span></span></span><span><span> * <span>@param $token </span></span></span><span><span> * <span>@return UserAppUser </span></span></span><span><span> * <span>@throws NoUserRoleException </span></span></span><span><span> */ </span></span><span> <span>private function userFromUserApp($user, $token) { </span></span><span> </span><span> <span>$roles = $this->extractRolesFromPermissions($user); </span></span><span> </span><span> <span>$options = array( </span></span><span> <span>'id' => $user->user_id, </span></span><span> <span>'username' => $user->login, </span></span><span> <span>'token' => $token, </span></span><span> <span>'firstName' => $user->first_name, </span></span><span> <span>'lastName' => $user->last_name, </span></span><span> <span>'email' => $user->email, </span></span><span> <span>'roles' => $roles, </span></span><span> <span>'properties' => $user->properties, </span></span><span> <span>'features' => $user->features, </span></span><span> <span>'permissions' => $user->permissions, </span></span><span> <span>'created' => $user->created_at, </span></span><span> <span>'locked' => !empty($user->locks), </span></span><span> <span>'last_logged_in' => $user->last_login_at, </span></span><span> <span>'last_heartbeat' => time(), </span></span><span> <span>); </span></span><span> </span><span> <span>return new UserAppUser($options); </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * Extracts the roles from the permissions list of a user </span></span><span><span> * </span></span><span><span> * <span>@param $user </span></span></span><span><span> * <span>@return <span>array</span> </span></span></span><span><span> * <span>@throws NoUserRoleException </span></span></span><span><span> */ </span></span><span> <span>private function extractRolesFromPermissions($user) { </span></span><span> <span>$permissions = get_object_vars($user->permissions); </span></span><span> <span>if (empty($permissions)) { </span></span><span> <span>throw new NoUserRoleException('There are no roles set up for your users.'); </span></span><span> <span>} </span></span><span> <span>$roles = array(); </span></span><span> <span>foreach ($permissions as $role => $permission) { </span></span><span> <span>if ($permission->value === TRUE) { </span></span><span> <span>$roles[] = $role; </span></span><span> <span>} </span></span><span> <span>} </span></span><span> </span><span> <span>if (empty($roles)) { </span></span><span> <span>throw new NoUserRoleException('This user has no roles enabled.'); </span></span><span> <span>} </span></span><span> </span><span> <span>return $roles; </span></span><span> <span>} </span></span><span><span>}</span></span>
Semblable à la classe Form Authenticator, nous injectons le client userApp.io dans cette classe en utilisant l'injection de dépendance et nous implémentons UserProviderInterface. Ce dernier exige que nous ayons 3 méthodes:
Remettons une seconde à notre classe Authenticator et voyons ce qui se passe exactement lorsque l'authentification avec userApp.io est réussie: nous appelons la méthode personnalisée LoadUserByLoginInfo () sur la classe du fournisseur d'utilisateurs qui prend un objet de résultat de connexion réussi de l'API et utilise son jeton d'authentification pour demander à l'API l'objet utilisateur connecté. Le résultat est enveloppé dans notre propre classe utilisateur local via les méthodes d'aide UserFromUserApp () et ExtractrolesFromperMissions (). Ce dernier est ma propre implémentation d'un moyen de traduire le concept d'autorisations dans userApp.io en rôles dans Symfony. Et nous jetons notre propre NouserRoleexception si userApp.io n'est pas configuré avec des autorisations pour les utilisateurs. Assurez-vous donc que vos utilisateurs de userApp.io ont des autorisations que vous souhaitez cartographier dans des rôles dans Symfony.
La classe d'exception est une extension simple de l'exception PHP par défaut:
<span><span><?php </span></span><span> </span><span><span>/** </span></span><span><span> * <span>@file AppBundle\Security\UserAppAuthenticator.php </span></span></span><span><span> */ </span></span><span> </span><span><span>namespace AppBundle<span>\Security</span>; </span></span><span> </span><span><span>use Symfony<span>\Component\HttpFoundation\Request</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\Authentication\SimpleFormAuthenticatorInterface</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\Authentication\Token\TokenInterface</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\Exception\AuthenticationException</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\User\UserProviderInterface</span>; </span></span><span><span>use UserApp<span>\API</span> as UserApp; </span></span><span><span>use UserApp<span>\Exceptions\ServiceException</span>; </span></span><span> </span><span><span>class UserAppAuthenticator implements SimpleFormAuthenticatorInterface </span></span><span><span>{ </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>@var UserApp </span></span></span><span><span> */ </span></span><span> <span>private $userAppClient; </span></span><span> </span><span> <span>public function __construct(UserApp $userAppClient) { </span></span><span> <span>$this->userAppClient = $userAppClient; </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>{@inheritdoc} </span></span></span><span><span> */ </span></span><span> <span>public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey) </span></span><span> <span>{ </span></span><span> </span><span> <span>try { </span></span><span> <span>$login = $this->userAppClient->user->login(array( </span></span><span> <span>"login" => $token->getUsername(), </span></span><span> <span>"password" => $token->getCredentials(), </span></span><span> <span>) </span></span><span> <span>); </span></span><span> </span><span> <span>// Load user from provider based on id </span></span><span> <span>$user = $userProvider->loadUserByLoginInfo($login); </span></span><span> <span>} catch(ServiceException $exception) { </span></span><span> <span>if ($exception->getErrorCode() == 'INVALID_ARGUMENT_LOGIN' || $exception->getErrorCode() == 'INVALID_ARGUMENT_PASSWORD') { </span></span><span> <span>throw new AuthenticationException('Invalid username or password'); </span></span><span> <span>} </span></span><span> <span>if ($exception->getErrorCode() == 'INVALID_ARGUMENT_APP_ID') { </span></span><span> <span>throw new AuthenticationException('Invalid app ID'); </span></span><span> <span>} </span></span><span> <span>} </span></span><span> <span>return new UserAppToken( </span></span><span> <span>$user, </span></span><span> <span>$user->getToken(), </span></span><span> <span>$providerKey, </span></span><span> <span>$user->getRoles() </span></span><span> <span>); </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>{@inheritdoc} </span></span></span><span><span> */ </span></span><span> <span>public function supportsToken(TokenInterface $token, $providerKey) </span></span><span> <span>{ </span></span><span> <span>return $token instanceof UserAppToken </span></span><span> <span>&& $token->getProviderKey() === $providerKey; </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>{@inheritdoc} </span></span></span><span><span> */ </span></span><span> <span>public function createToken(Request $request, $username, $password, $providerKey) </span></span><span> <span>{ </span></span><span> <span>return new UserAppToken($username, $password, $providerKey); </span></span><span> <span>} </span></span><span><span>}</span></span>
Retour à notre authentificateur à nouveau, nous voyons que si l'authentification avec userApp.io est réussie, un objet classé appuseur est construit par le fournisseur d'utilisateurs contenant toutes les informations nécessaires sur l'utilisateur. En ayant cet objet, nous devons l'ajouter à une nouvelle instance de la classe UserAppToken et le renvoyer.
Donc, cela se produit à partir du moment où un utilisateur essaie de se connecter:
La méthode RefreshUser () sur le fournisseur d'utilisateurs est également très importante. Cette méthode est responsable de la récupération d'une nouvelle instance de l'utilisateur actuellement connecté sur chaque actualisation de la page authentifiée. Ainsi, chaque fois que l'utilisateur authentifié va à l'une des pages à l'intérieur du pare-feu, cette méthode est déclenchée. Le but est d'hydrater l'objet utilisateur avec toute modification du stockage qui aurait pu se produire entre-temps.
De toute évidence, nous devons garder les appels API au minimum, mais c'est une bonne occasion d'augmenter le temps d'authentification de userApp.io en envoyant une demande de battement de cœur. Par défaut (mais configurable), chaque jeton utilisateur authentifié est valide pendant 60 minutes, mais en envoyant une demande de battement de cœur, cela est prolongé de 20 minutes.
C'est un endroit idéal pour remplir également deux autres fonctions:
Si vous le souhaitez, vous pouvez également faire une demande API et mettre à jour l'objet utilisateur avec des données de userApp.io ici, mais je trouve que cela n'a pas beaucoup de sens pour la plupart des cas d'utilisation. Les données peuvent être mises à jour lorsque l'utilisateur se déconnecte et revient dans la prochaine fois. Mais selon les besoins, cela peut être facilement fait ici. Bien que gardez à l'esprit les implications de performance et le coût de nombreux appels d'API vers userApp.io.
et fondamentalement, c'est le nœud de notre logique d'authentification.
Créons également la classe UserAppuser dont nous avons parlé plus tôt:
<span><span><?php </span></span><span> </span><span><span>/** </span></span><span><span> * <span>@file AppBundle\Security\UserAppAuthenticator.php </span></span></span><span><span> */ </span></span><span> </span><span><span>namespace AppBundle<span>\Security</span>; </span></span><span> </span><span><span>use Symfony<span>\Component\HttpFoundation\Request</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\Authentication\SimpleFormAuthenticatorInterface</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\Authentication\Token\TokenInterface</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\Exception\AuthenticationException</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\User\UserProviderInterface</span>; </span></span><span><span>use UserApp<span>\API</span> as UserApp; </span></span><span><span>use UserApp<span>\Exceptions\ServiceException</span>; </span></span><span> </span><span><span>class UserAppAuthenticator implements SimpleFormAuthenticatorInterface </span></span><span><span>{ </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>@var UserApp </span></span></span><span><span> */ </span></span><span> <span>private $userAppClient; </span></span><span> </span><span> <span>public function __construct(UserApp $userAppClient) { </span></span><span> <span>$this->userAppClient = $userAppClient; </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>{@inheritdoc} </span></span></span><span><span> */ </span></span><span> <span>public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey) </span></span><span> <span>{ </span></span><span> </span><span> <span>try { </span></span><span> <span>$login = $this->userAppClient->user->login(array( </span></span><span> <span>"login" => $token->getUsername(), </span></span><span> <span>"password" => $token->getCredentials(), </span></span><span> <span>) </span></span><span> <span>); </span></span><span> </span><span> <span>// Load user from provider based on id </span></span><span> <span>$user = $userProvider->loadUserByLoginInfo($login); </span></span><span> <span>} catch(ServiceException $exception) { </span></span><span> <span>if ($exception->getErrorCode() == 'INVALID_ARGUMENT_LOGIN' || $exception->getErrorCode() == 'INVALID_ARGUMENT_PASSWORD') { </span></span><span> <span>throw new AuthenticationException('Invalid username or password'); </span></span><span> <span>} </span></span><span> <span>if ($exception->getErrorCode() == 'INVALID_ARGUMENT_APP_ID') { </span></span><span> <span>throw new AuthenticationException('Invalid app ID'); </span></span><span> <span>} </span></span><span> <span>} </span></span><span> <span>return new UserAppToken( </span></span><span> <span>$user, </span></span><span> <span>$user->getToken(), </span></span><span> <span>$providerKey, </span></span><span> <span>$user->getRoles() </span></span><span> <span>); </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>{@inheritdoc} </span></span></span><span><span> */ </span></span><span> <span>public function supportsToken(TokenInterface $token, $providerKey) </span></span><span> <span>{ </span></span><span> <span>return $token instanceof UserAppToken </span></span><span> <span>&& $token->getProviderKey() === $providerKey; </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>{@inheritdoc} </span></span></span><span><span> */ </span></span><span> <span>public function createToken(Request $request, $username, $password, $providerKey) </span></span><span> <span>{ </span></span><span> <span>return new UserAppToken($username, $password, $providerKey); </span></span><span> <span>} </span></span><span><span>}</span></span>
Rien de particulier ici, nous mappons simplement certaines données de userApp.io et implémentant certaines des méthodes requises par l'interface. De plus, nous avons ajouté le trafic verrouillé / déverrouillé.
La dernière classe que nous devons créer est celle qui traite de l'exploitation de l'utilisateur à partir de userApp.io lorsqu'il déconnecte de Symfony.
<span><span><?php </span></span><span> </span><span><span>/** </span></span><span><span> * <span>@file AppBundle\Security\UserAppToken.php </span></span></span><span><span> */ </span></span><span> </span><span><span>namespace AppBundle<span>\Security</span>; </span></span><span> </span><span><span>use Symfony<span>\Component\Security\Core\Authentication\Token\UsernamePasswordToken</span>; </span></span><span> </span><span><span>class UserAppToken extends UsernamePasswordToken { </span></span><span> </span><span><span>}</span></span>
Ici encore, nous injectons le client UserApp.io PHP et depuis que nous implémentons la LogouthandlerInterface, nous devons avoir une méthode Logout (). Tout ce que nous y faisons est de loger l'utilisateur à partir de userApp.io s'il est toujours connecté.
Maintenant que nous avons nos cours, il est temps de les déclarer comme services et de les utiliser dans notre système d'authentification. Voici nos déclarations de services basées sur YML:
<span><span><?php </span></span><span> </span><span><span>/** </span></span><span><span> * <span>@file AppBundle\Security\UserAppProvider.php </span></span></span><span><span> */ </span></span><span> </span><span><span>namespace AppBundle<span>\Security</span>; </span></span><span> </span><span><span>use Symfony<span>\Component\Security\Core\Exception\AuthenticationException</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\Exception\UsernameNotFoundException</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\User\UserProviderInterface</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\User\UserInterface</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\Exception\UnsupportedUserException</span>; </span></span><span><span>use UserApp<span>\API</span> as UserApp; </span></span><span><span>use UserApp<span>\Exceptions\ServiceException</span>; </span></span><span><span>use AppBundle<span>\Security\Exception\NoUserRoleException</span>; </span></span><span><span>use AppBundle<span>\Security\UserAppUser</span>; </span></span><span> </span><span><span>class UserAppProvider implements UserProviderInterface </span></span><span><span>{ </span></span><span> <span>/** </span></span><span><span> * <span>@var UserApp </span></span></span><span><span> */ </span></span><span> <span>private $userAppClient; </span></span><span> </span><span> <span>public function __construct(UserApp $userAppClient) { </span></span><span> <span>$this->userAppClient = $userAppClient; </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>{@inheritdoc} </span></span></span><span><span> */ </span></span><span> <span>public function loadUserByUsername($username) </span></span><span> <span>{ </span></span><span> <span>// Empty for now </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>{@inheritdoc} </span></span></span><span><span> */ </span></span><span> <span>public function refreshUser(UserInterface $user) </span></span><span> <span>{ </span></span><span> <span>if (!$user instanceof UserAppUser) { </span></span><span> <span>throw new UnsupportedUserException( </span></span><span> <span>sprintf('Instances of "%s" are not supported.', get_class($user)) </span></span><span> <span>); </span></span><span> <span>} </span></span><span> </span><span> <span>try { </span></span><span> <span>$api = $this->userAppClient; </span></span><span> <span>$api->setOption('token', $user->getToken()); </span></span><span> <span>$api->token->heartbeat(); </span></span><span> <span>$user->unlock(); </span></span><span> <span>} </span></span><span> <span>catch (ServiceException $exception) { </span></span><span> <span>if ($exception->getErrorCode() == 'INVALID_CREDENTIALS') { </span></span><span> <span>throw new AuthenticationException('Invalid credentials'); </span></span><span> <span>} </span></span><span> <span>if ($exception->getErrorCode() == 'AUTHORIZATION_USER_LOCKED') { </span></span><span> <span>$user->lock(); </span></span><span> <span>} </span></span><span> <span>} </span></span><span> </span><span> <span>return $user; </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>{@inheritdoc} </span></span></span><span><span> */ </span></span><span> <span>public function supportsClass($class) </span></span><span> <span>{ </span></span><span> <span>return $class === 'AppBundle\Security\UserAppUser'; </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * </span></span><span><span> * Loads a user from UserApp.io based on a successful login response. </span></span><span><span> * </span></span><span><span> * <span>@param $login </span></span></span><span><span> * <span>@return UserAppUser </span></span></span><span><span> * <span>@throws NoUserRoleException </span></span></span><span><span> */ </span></span><span> <span>public function loadUserByLoginInfo($login) { </span></span><span> </span><span> <span>try { </span></span><span> <span>$api = $this->userAppClient; </span></span><span> <span>$api->setOption('token', $login->token); </span></span><span> <span>$users = $api->user->get(); </span></span><span> <span>} catch(ServiceException $exception) { </span></span><span> <span>if ($exception->getErrorCode() == 'INVALID_ARGUMENT_USER_ID') { </span></span><span> <span>throw new UsernameNotFoundException(sprintf('User with the id "%s" not found.', $login->user_id)); </span></span><span> <span>} </span></span><span> <span>} </span></span><span> </span><span> <span>if (!empty($users)) { </span></span><span> <span>return $this->userFromUserApp($users[0], $login->token); </span></span><span> <span>} </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * Creates a UserAppUser from a user response from UserApp.io </span></span><span><span> * </span></span><span><span> * <span>@param $user </span></span></span><span><span> * <span>@param $token </span></span></span><span><span> * <span>@return UserAppUser </span></span></span><span><span> * <span>@throws NoUserRoleException </span></span></span><span><span> */ </span></span><span> <span>private function userFromUserApp($user, $token) { </span></span><span> </span><span> <span>$roles = $this->extractRolesFromPermissions($user); </span></span><span> </span><span> <span>$options = array( </span></span><span> <span>'id' => $user->user_id, </span></span><span> <span>'username' => $user->login, </span></span><span> <span>'token' => $token, </span></span><span> <span>'firstName' => $user->first_name, </span></span><span> <span>'lastName' => $user->last_name, </span></span><span> <span>'email' => $user->email, </span></span><span> <span>'roles' => $roles, </span></span><span> <span>'properties' => $user->properties, </span></span><span> <span>'features' => $user->features, </span></span><span> <span>'permissions' => $user->permissions, </span></span><span> <span>'created' => $user->created_at, </span></span><span> <span>'locked' => !empty($user->locks), </span></span><span> <span>'last_logged_in' => $user->last_login_at, </span></span><span> <span>'last_heartbeat' => time(), </span></span><span> <span>); </span></span><span> </span><span> <span>return new UserAppUser($options); </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * Extracts the roles from the permissions list of a user </span></span><span><span> * </span></span><span><span> * <span>@param $user </span></span></span><span><span> * <span>@return <span>array</span> </span></span></span><span><span> * <span>@throws NoUserRoleException </span></span></span><span><span> */ </span></span><span> <span>private function extractRolesFromPermissions($user) { </span></span><span> <span>$permissions = get_object_vars($user->permissions); </span></span><span> <span>if (empty($permissions)) { </span></span><span> <span>throw new NoUserRoleException('There are no roles set up for your users.'); </span></span><span> <span>} </span></span><span> <span>$roles = array(); </span></span><span> <span>foreach ($permissions as $role => $permission) { </span></span><span> <span>if ($permission->value === TRUE) { </span></span><span> <span>$roles[] = $role; </span></span><span> <span>} </span></span><span> <span>} </span></span><span> </span><span> <span>if (empty($roles)) { </span></span><span> <span>throw new NoUserRoleException('This user has no roles enabled.'); </span></span><span> <span>} </span></span><span> </span><span> <span>return $roles; </span></span><span> <span>} </span></span><span><span>}</span></span>
Le premier est la bibliothèque UserApp.io PHP à laquelle nous transmettons notre ID d'application sous la forme d'une référence à un paramètre. Vous devrez avoir un paramètre appelé userApp_id avec votre ID d'application userApp.io.
Les trois autres sont l'authentificateur de formulaire, le fournisseur d'utilisateurs et les classes de déconnexion que nous avons écrites plus tôt. Et comme vous vous en souvenez, chacun accepte un paramètre de son constructeur sous la forme du client userApp.io défini comme le premier service.
Ensuite, il est temps d'utiliser ces services dans notre système de sécurité, alors modifiez le fichier Security.yml et effectuez ce qui suit:
Sous la clé des fournisseurs, ajoutez ce qui suit:
<span><span><?php </span></span><span> </span><span><span>/** </span></span><span><span> * <span>@file AppBundle\Security\Exception\NoUserRoleException.php </span></span></span><span><span> */ </span></span><span> </span><span><span>namespace AppBundle<span>\Security\Exception</span>; </span></span><span> </span><span><span>class NoUserRoleException extends <span>\Exception</span> { </span></span><span> </span><span><span>}</span></span>
Ici, nous spécifions que notre application a également ce fournisseur d'utilisateurs afin qu'il puisse l'utiliser.
Sous la clé de pare-feu, ajoutez ce qui suit:
<span><span><?php </span></span><span> </span><span><span>/** </span></span><span><span> * <span>@file AppBundle\Security\UserAppAuthenticator.php </span></span></span><span><span> */ </span></span><span> </span><span><span>namespace AppBundle<span>\Security</span>; </span></span><span> </span><span><span>use Symfony<span>\Component\HttpFoundation\Request</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\Authentication\SimpleFormAuthenticatorInterface</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\Authentication\Token\TokenInterface</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\Exception\AuthenticationException</span>; </span></span><span><span>use Symfony<span>\Component\Security\Core\User\UserProviderInterface</span>; </span></span><span><span>use UserApp<span>\API</span> as UserApp; </span></span><span><span>use UserApp<span>\Exceptions\ServiceException</span>; </span></span><span> </span><span><span>class UserAppAuthenticator implements SimpleFormAuthenticatorInterface </span></span><span><span>{ </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>@var UserApp </span></span></span><span><span> */ </span></span><span> <span>private $userAppClient; </span></span><span> </span><span> <span>public function __construct(UserApp $userAppClient) { </span></span><span> <span>$this->userAppClient = $userAppClient; </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>{@inheritdoc} </span></span></span><span><span> */ </span></span><span> <span>public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey) </span></span><span> <span>{ </span></span><span> </span><span> <span>try { </span></span><span> <span>$login = $this->userAppClient->user->login(array( </span></span><span> <span>"login" => $token->getUsername(), </span></span><span> <span>"password" => $token->getCredentials(), </span></span><span> <span>) </span></span><span> <span>); </span></span><span> </span><span> <span>// Load user from provider based on id </span></span><span> <span>$user = $userProvider->loadUserByLoginInfo($login); </span></span><span> <span>} catch(ServiceException $exception) { </span></span><span> <span>if ($exception->getErrorCode() == 'INVALID_ARGUMENT_LOGIN' || $exception->getErrorCode() == 'INVALID_ARGUMENT_PASSWORD') { </span></span><span> <span>throw new AuthenticationException('Invalid username or password'); </span></span><span> <span>} </span></span><span> <span>if ($exception->getErrorCode() == 'INVALID_ARGUMENT_APP_ID') { </span></span><span> <span>throw new AuthenticationException('Invalid app ID'); </span></span><span> <span>} </span></span><span> <span>} </span></span><span> <span>return new UserAppToken( </span></span><span> <span>$user, </span></span><span> <span>$user->getToken(), </span></span><span> <span>$providerKey, </span></span><span> <span>$user->getRoles() </span></span><span> <span>); </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>{@inheritdoc} </span></span></span><span><span> */ </span></span><span> <span>public function supportsToken(TokenInterface $token, $providerKey) </span></span><span> <span>{ </span></span><span> <span>return $token instanceof UserAppToken </span></span><span> <span>&& $token->getProviderKey() === $providerKey; </span></span><span> <span>} </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>{@inheritdoc} </span></span></span><span><span> */ </span></span><span> <span>public function createToken(Request $request, $username, $password, $providerKey) </span></span><span> <span>{ </span></span><span> <span>return new UserAppToken($username, $password, $providerKey); </span></span><span> <span>} </span></span><span><span>}</span></span>
Ce qui se passe ici, c'est que nous définissons une zone sécurisée simple qui utilise le type d'authentification simple_form avec notre authentificateur. Sous la clé de déconnexion, nous ajoutons un gestionnaire à appeler (notre classe UserApplogout définie comme un service). Le reste est une configuration de sécurité Symfony régulière, alors assurez-vous que vous avez un formulaire de connexion affiché sur l'itinéraire de connexion, etc. Consultez la documentation à ce sujet pour plus d'informations.
Et c'est tout. En utilisant l'authentification Simple_Form avec notre authentificateur de formulaire personnalisé et notre fournisseur d'utilisateurs (ainsi qu'un gestionnaire de déconnexion facultatif), nous avons implémenté notre propre mécanisme d'authentification Symfony basé sur userApp.io.
Dans cet article, nous avons vu comment implémenter une authentification de formulaire Symfony personnalisée à l'aide du service UserApp.io et de l'API en tant que fournisseur d'utilisateurs. Nous avons traversé beaucoup de code, ce qui signifiait une très brève explication du code lui-même. J'ai plutôt essayé d'expliquer le processus d'authentification avec Symfony en créant une solution personnalisée qui prend en compte la façon dont nous pouvons interagir avec userApp.io.
Si vous avez suivi et implémenté cette méthode à l'intérieur de votre bundle et que vous souhaitez l'utiliser comme ceci, allez-y. Vous avez également la possibilité d'utiliser la bibliothèque que j'ai créée qui a une configuration très rapide et facile décrite sur la page GitHub. Je recommande ce dernier car je prévois de le développer et de le maintenir afin que vous puissiez toujours obtenir une version mise à jour si des bogues sont supprimés ou des fonctionnalités introduites (j'espère pas l'inverse).
Si vous souhaitez y contribuer, vous êtes les bienvenus. J'apprécie également de me faire savoir si vous trouvez des problèmes ou pensez qu'il existe de meilleures façons d'atteindre des objectifs similaires.
L'intégration de l'utilisateur userApp.io à Symfony2 pour l'authentification de l'utilisateur implique quelques étapes. Tout d'abord, vous devez installer la bibliothèque UserApp à l'aide de Composer. Ensuite, vous devez configurer le service UserApp dans votre projet Symfony2. Cela implique la configuration de la touche API UserApp et la configuration du service UserApp dans le fichier Services.yml. Après cela, vous pouvez utiliser le service UserApp dans vos contrôleurs pour authentifier les utilisateurs.
userApp.io fournit un certain nombre de Avantages pour l'authentification des utilisateurs dans Symfony2. Il simplifie le processus de gestion des utilisateurs en fournissant une solution prête à l'authentification, l'enregistrement, la réinitialisation du mot de passe, etc. Il fournit également une solution sécurisée et évolutive pour l'authentification des utilisateurs, qui peut être très bénéfique pour les applications à grande échelle.
userApp.io fournit une fonctionnalité appelée «Rôles utilisateur» qui vous permet de gérer les rôles et les autorisations utilisateur. Vous pouvez définir différents rôles et les attribuer aux utilisateurs. Ensuite, vous pouvez vérifier le rôle de l'utilisateur dans vos contrôleurs Symfony2 pour contrôler l'accès à différentes parties de votre application.
userApp.io Fournit une fonctionnalité appelée «Enregistrement des utilisateurs» qui vous permet de gérer l'enregistrement des utilisateurs dans votre application Symfony2. Vous pouvez utiliser le service UserApp dans vos contrôleurs pour enregistrer de nouveaux utilisateurs. Le service UserApp gérera le processus d'enregistrement, y compris la validation de l'e-mail et du mot de passe de l'utilisateur, et la création d'un nouveau compte utilisateur.
UserApp .io fournit une fonctionnalité appelée «réinitialisation de mot de passe» qui vous permet de gérer la réinitialisation du mot de passe dans votre application Symfony2. Vous pouvez utiliser le service UserApp dans vos contrôleurs pour réinitialiser le mot de passe d'un utilisateur. Le service userApp gérera le processus de réinitialisation du mot de passe, y compris l'envoi d'un e-mail de réinitialisation de mot de passe à l'utilisateur.
userApp.io fournit Une fonctionnalité appelée «Gestion des erreurs» qui vous permet de gérer les erreurs d'authentification des utilisateurs dans votre application Symfony2. Vous pouvez utiliser le service UserApp dans vos contrôleurs pour attraper et gérer les erreurs d'authentification. Le service UserApp fournira des messages d'erreur détaillés que vous pouvez utiliser pour déboguer et résoudre les problèmes d'authentification.
userApp.io fournit Un certain nombre d'options de personnalisation pour le processus d'authentification de l'utilisateur. Vous pouvez personnaliser le formulaire de connexion, le formulaire d'enregistrement, le formulaire de réinitialisation du mot de passe, etc. Vous pouvez également personnaliser le processus d'authentification de l'utilisateur en ajoutant des champs personnalisés au profil utilisateur, ou en implémentant la logique d'authentification personnalisée.
userApp. IO fournit un certain nombre de fonctionnalités de sécurité qui peuvent vous aider à sécuriser votre application Symfony2. Il fournit une authentification utilisateur sécurisée, un stockage de mot de passe sécurisé et une gestion sécurisée des utilisateurs. Il fournit également des fonctionnalités telles que l'authentification à deux facteurs et la liste blanche IP qui peuvent améliorer davantage la sécurité de votre application.
UserApp .io fournit une fonctionnalité appelée «migration de données» qui vous permet de migrer vos données utilisateur existantes vers userApp.io. Vous pouvez utiliser l'API UserApp pour importer vos données utilisateur existantes dans userApp.io. L'API UserApp fournit un certain nombre de points de terminaison que vous pouvez utiliser pour importer des données utilisateur, y compris les profils d'utilisateur, les rôles utilisateur et les autorisations utilisateur.
userApp.io fournit un certain nombre d'outils de dépannage qui peuvent vous aider à résoudre les problèmes avec userApp.io dans Symfony2. Il fournit des messages d'erreur détaillés, une journalisation et des outils de débogage. Vous pouvez également utiliser l'API UserApp pour résoudre les problèmes avec le service UserApp. L'API UserApp fournit un certain nombre de points de terminaison que vous pouvez utiliser pour déboguer et résoudre les problèmes avec le service UserApp.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!