userApp.io ist ein praktisches Tool und eine API von Benutzerverwaltung. Es bietet eine Weboberfläche, mit der Sie mit Benutzerkonten (und den vielen Funktionen, die dies mit sich bringt) und eine API, um sie in Ihre eigene Webanwendung einzuschließen. Der Zweck dieses Dienstes ist es, es einfacher und sicherer zu machen, die Benutzerauthentifizierung zu verwalten, indem Sie sich auf Ihrem eigenen Server nicht darum kümmern müssen.
Es verfügt über SDKs und verschiedene Wrapper für viele Programmiersprachen und Frameworks, und der Preis ist erschwinglich. Ja, es ist mit einem Preis verbunden, aber Sie können mit ziemlich vielen Dingen, mit denen Sie herumspielen können, frei beginnen. Ich empfehle, ihre Funktionsseite zu überprüfen, um weitere Informationen zu erhalten. Außerdem ist es sehr einfach, ein Konto zu erstellen und mit dem Erstellen von Benutzern zu experimentieren, ihre Profile usw. Eigenschaften hinzuzufügen. Ich empfehle Ihnen, dies auch zu überprüfen, wenn Sie es noch nicht getan haben.
In diesem Artikel werden wir uns untersuchen, wie wir einen Symfony2 -Authentifizierungsmechanismus implementieren können, der userApp.io nutzt. Der Code, den wir schreiben, finden Sie auch in dieser kleinen Bibliothek, die ich (derzeit in Dev) erstellt habe, die Sie ausprobieren können. Um es in Ihrer Symfony -App zu installieren, befolgen Sie einfach die Anweisungen auf GitHub.
Um mit dem UserApp.io -Dienst zu kommunizieren, verwenden wir ihre PHP -Bibliothek. Stellen Sie sicher, dass Sie dies in der Composer.json -Datei Ihrer Symfony -Anwendung benötigen, wie auf ihrer Github -Seite angewiesen.
, um userApp.io -Benutzer mit unserer Symfony -App zu authentifizieren, erstellen wir einige Klassen:
Form Authenticator
<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>
Erstens erstellen wir die wichtigste Klasse, den Formular Authenticator (in einem Sicherheits-/ Ordner unserer besten Practice namens AppBundle). Hier ist der Code, ich werde ihn danach erklären:
Wie Sie sehen, implementieren wir das SimpleFormAuthenticatorInterface und haben folglich 3 Methoden und einen Konstruktor. Letzteres nimmt eine Abhängigkeit als sofortiger userApp.io -Client (über den Service Container übergeben, aber in einer Minute mehr).
.
Diese Klasse wird von Symfony verwendet, wenn ein Benutzer versucht, sich mit der Anwendung anzumelden und zu authentifizieren. Das erste, was passiert, ist, dass CreateToken () aufgerufen wird. Diese Methode muss ein Authentifizierungs -Token zurückgeben, das den eingereichten Benutzernamen und das eingereichte Passwort kombiniert. In unserem Fall wird es eine Instanz der benutzerdepToken -Klasse sein, die wir in einem Moment definieren werden.
Dann wird die SupportToken () -Methode aufgerufen, um zu überprüfen, ob diese Klasse das von CreateToken () zurückgegebene Token unterstützt. Hier stellen wir nur sicher, dass wir für unseren Token -Typ zutreffen.
<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>
Token -Klasse
Wie Sie sehen, ist dies nur eine Erweiterung der usernamepassword -Class, um zu benennen, genauer zu sein (da wir ein Token anstelle eines Passworts speichern).
<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>
Nächst Ähnlich wie bei der Form der Form der Authentikatoren injizieren wir den userApp.io -Client mithilfe der Abhängigkeitsinjektion in diese Klasse und implementieren das UserProviderInterface. Letzteres erfordert, dass wir 3 Methoden haben:
Lassen Sie uns eine Sekunde in unsere Authenticator -Klasse zurückgeben und sehen, was genau passiert, wenn die Authentifizierung mit userApp.io erfolgreich ist Verwendet sein Authentifizierungs-Token, um das angemeldete Benutzerobjekt von der API zurückzufordern. Das Ergebnis wird über den Benutzer aus dem Benutzer aus der lokalen örtlichen UserAppuser über den BenutzerfromuserApp () und die ExtractrolesFrompermissions () Helper -Methoden eingewickelt. Letzteres ist meine eigene Implementierung einer Möglichkeit, das Konzept der Berechtigungen in userApp.io in Symfony zu übersetzen. Und wir werfen unsere eigene Nouserrolexception, wenn der userApp.io nicht mit Berechtigungen für die Benutzer eingerichtet ist. Stellen Sie also sicher, dass Ihre Benutzer in userApp.io Berechtigungen haben, die Sie in Symfony zuordnen möchten.
Die Ausnahmeklasse ist eine einfache Erweiterung aus der Standard -PHP -Ausnahme:
<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>
Zurück zu unserem Authenticator sehen wir, dass ein Benutzeranpassungsobjekt, der vom Benutzeranbieter mit allen erforderlichen Informationen über den Benutzer enthält, wenn die Authentifizierung mit userApp.io erfolgreich ist. Mit diesem Objekt müssen wir es einer neuen Instanz der Benutzerapptoken -Klasse hinzufügen und zurückgeben.
Im Grunde genommen geschieht dies ab dem Moment, in dem ein Benutzer versucht, sich anzumelden:
Die Methode refreshuser () am Benutzeranbieter ist ebenfalls sehr wichtig. Diese Methode ist dafür verantwortlich, eine neue Instanz des aktuell angemeldeten Benutzer auf jeder authentifizierten Seiten -Aktualisierung abzurufen. Wenn der authentifizierte Benutzer auf eine der Seiten in der Firewall geht, wird diese Methode ausgelöst. Es geht darum, das Benutzerobjekt mit Änderungen des Speichers zu hydratisieren, die in der Zwischenzeit möglicherweise stattgefunden haben.
Offensichtlich müssen wir API -Anrufe auf ein Minimum führen, aber dies ist eine gute Gelegenheit, die Authentifizierungszeit von userApp.io zu erhöhen, indem eine Herzschlaganfrage gesendet wird. Standardmäßig (aber konfigurierbar) ist jedes authentifizierte Benutzer -Token für 60 Minuten gültig, aber durch Senden einer Herzschlaganforderung wird dies um 20 Minuten verlängert.
Dies ist ein großartiger Ort, um auch zwei weitere Funktionen auszuführen:
Wenn Sie möchten, können Sie hier eine API -Anforderung stellen und das Benutzerobjekt mit Daten von userApp.io aktualisieren, aber ich finde, dass es für die meisten Anwendungsfälle nicht viel Sinn macht. Daten können aktualisiert werden, wenn sich der Benutzer im nächsten Mal anmeldet und zurück anmeldet. Abhängig von den Bedürfnissen kann dies jedoch einfach hier erfolgen. Beachten Sie zwar die Auswirkungen auf die Leistung und die Kosten vieler API -Aufrufe bei userApp.io.
Und im Grunde ist das der Kern unserer Authentifizierungslogik.
Erstellen wir auch die UserAppuser -Klasse, über die wir früher gesprochen haben:
<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>
Nichts Besonderes hier, wir machen nur einige Daten von userApp.io ab und implementieren einige der von der Schnittstelle erforderlichen Methoden. Zusätzlich haben wir den gesperrten/entsperrten Flagger hinzugefügt.
Die letzte Klasse, die wir erstellen müssen, ist diejenige, die sich mit der Protokollierung des Benutzers von userApp.io befasst, wenn sie sich von Symfony anmelden.
<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>
Hier injizieren wir den userApp.io -PHP -Client und da wir das LogoutHerDerInterface implementieren, müssen wir über eine mental () -Methode () verfügen. Alles, was wir darin tun, ist, den Benutzer von userApp.io abzumelden, wenn er noch angemeldet ist.
Jetzt, da wir unsere Klassen haben, ist es an der Zeit, sie als Dienste zu erklären und in unserem Authentifizierungssystem zu nutzen. Hier sind unsere YML -basierten Serviceerklärungen:
<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>
Die erste ist die userApp.io -PHP -Bibliothek, an die wir unsere Anwendungs -ID in Form eines Verweiss auf einen Parameter übergeben. Sie benötigen einen Parameter namens userApp_id mit Ihrer userApp.io -App -ID.
Die anderen drei sind der Formular Authenticator, Benutzeranbieter und Abmeldungsklassen, die wir zuvor geschrieben haben. Und wie Sie sich erinnern, akzeptiert jeder einen Parameter in seinem Konstruktor in Form des als ersten Dienstes definierten userApp.io -Clients.
Nächst
<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>
<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>
Was hier passiert, ist, dass wir einen einfachen sicheren Bereich definieren, der die Authentifizierungstyp von Simple_form mit unserem Authentikator verwendet. Unter dem Abmeldetaste fügen wir einen Handler hinzu, der aufgerufen werden soll (unsere als Dienstleistung definierte Benutzeranplout -Klasse). Der Rest ist ein regelmäßiges Symfony Security -Setup. Stellen Sie also sicher, dass auf der Anmelderoute ein Anmeldeformular angezeigt wird. Weitere Informationen finden Sie in der Dokumentation.
Und das ist alles. Durch die Verwendung der Simple_Form -Authentifizierung mit unserem benutzerdefinierten Formular Authenticator und Benutzeranbieter (zusammen mit einem optionalen Abmeldungshandler) haben wir unseren eigenen Symfony -Authentifizierungsmechanismus von UserApp.io implementiert.
In diesem Artikel haben wir gesehen, wie eine benutzerdefinierte Symfony -Formauthentifizierung mit dem UserApp.io -Dienst und API als Benutzeranbieter implementiert werden kann. Wir haben eine Menge Code durchgemacht, was eine sehr kurze Erklärung des Code selbst bedeutete. Vielmehr habe ich versucht, den Authentifizierungsprozess mit Symfony zu erklären, indem ich eine benutzerdefinierte Lösung erstellt, die die Art und Weise berücksichtigt, wie wir mit userApp.io interagieren können.
Wenn Sie diese Methode in Ihrem Bundle befolgt und implementiert haben und sie so verwenden möchten, machen Sie weiter. Sie haben auch die Möglichkeit, die von mir erstellte Bibliothek zu verwenden, die ein sehr schnelles und einfaches Setup auf der GitHub -Seite enthält. Ich empfehle letztere, da ich vorhabe, sie zu entwickeln und zu pflegen, damit Sie immer eine aktualisierte Version erhalten, wenn Fehler entfernt oder Funktionen eingeführt werden (hoffe nicht umgekehrt).
Wenn Sie dazu beitragen möchten, sind Sie sehr willkommen. Ich schätze es auch, mich wissen zu lassen, wenn Sie Probleme finden oder glauben, dass es bessere Möglichkeiten gibt, ähnliche Ziele zu erreichen.
Das obige ist der detaillierte Inhalt vonBenutzerauthentifizierung in Symfony2 mit userApp.io. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!