CakePHP 4.x Nutzer Authentifizierungs Tutorial
6 October 2020In diesem Blogartikel wird beschrieben, wie Ihr eine simple Nutzer Authentifizierung für CakePHP mittels des offiziell unterstützen Authentication Plugins aufbaut.
Authentifizierung in Web Anwendungen ist der Nachweis der Identität eines Nutzers, d.h. ist der Nutzer der, der er vorgibt zu sein. Authentifizierung findet meist über Nutzername/Passwort, Sessions/Cookies oder JWT/OAuth statt. Ob ein bestimmter Nutzer auf eine bestimmte Resource (z.B. Website) zugreifen darf ist hingegen Teil der Authorisierung und wird in einem weiteren Artikel beschrieben.
Grundlage dieses Tutorials ist das CakePHP Quickstart Tutorial.
Installation des Plugins
Installiert das Plugin mit Composer im root Verzeichnis eurer App:
composer require cakephp/authentication:^2.0
Das Plugin in eure Application laden:
// src/Application.php
public function bootstrap(): void
{
// Call parent to load bootstrap from files.
parent::bootstrap();
...
// Load more plugins here
$this->addPlugin('Authentication');
}
Middleware
Das Authentication Plugin wird mittels einer PSR-7 Middleware integriert; diese prüft bei jedem Request die Identität des Nutzers mittels eines Authentifikators (z.B. Form Authenticator mit Nutzername/Passwort).
Um die Middleware nutzen zu können muss unsere Application das AuthenticationProviderInterface
implementieren und die Middleware in der middleware()
Methode hinzugefügt werden:
// src/Application.php
use Authentication\AuthenticationServiceInterface;
use Authentication\AuthenticationServiceProviderInterface;
use Authentication\Middleware\AuthenticationMiddleware;
use Cake\Core\Configure;
use Cake\Core\Exception\MissingPluginException;
use Cake\Error\Middleware\ErrorHandlerMiddleware;
use Cake\Http\BaseApplication;
use Cake\Http\Middleware\BodyParserMiddleware;
use Cake\Http\Middleware\CsrfProtectionMiddleware;
use Cake\Http\MiddlewareQueue;
use Cake\Routing\Middleware\AssetMiddleware;
use Cake\Routing\Middleware\RoutingMiddleware;
use Psr\Http\Message\ServerRequestInterface;
class Application extends BaseApplication implements AuthenticationServiceProviderInterface
{
/**
* Load all the application configuration and bootstrap logic.
*
* @return void
*/
public function bootstrap(): void
{
// Call parent to load bootstrap from files.
parent::bootstrap();
...
// Load more plugins here
$this->addPlugin('Authentication');
}
/**
* Setup the middleware queue your application will use.
*
* @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to setup.
* @return \Cake\Http\MiddlewareQueue The updated middleware queue.
*/
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
{
$middlewareQueue
// load other Middleware
...
// Authentication Middleware
->add(new AuthenticationMiddleware($this));
return $middlewareQueue;
}
public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
{
// TODO: Implement getAuthenticationService() method.
}
}
Mehr zur Authentication Middleware und zu CakePHP Middleware.
Als nächstes implementieren wir die getAuthenticationService()
Methode:
// src/Application.php
use Authentication\AuthenticationService;
use Authentication\Identifier\IdentifierInterface;
...
/**
* Returns a service provider instance.
*
* @param \Psr\Http\Message\ServerRequestInterface $request Request
* @return \Authentication\AuthenticationServiceInterface
*/
public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
{
// Create AuthenticationService
$service = new AuthenticationService();
// Define where users should be redirected to when they are not authenticated
$service->setConfig([
'unauthenticatedRedirect' => Router::url(['controller' => 'Users', 'action' => 'login']),
'queryParam' => 'redirect',
]);
// Fields in your db to match against
$fields = [
IdentifierInterface::CREDENTIAL_USERNAME => 'email',
IdentifierInterface::CREDENTIAL_PASSWORD => 'password'
];
// Load the authenticators. Session should be first.
$service->loadAuthenticator('Authentication.Session');
$service->loadAuthenticator('Authentication.Form', [
'fields' => $fields,
'loginUrl' => Router::url(['controller' => 'Users', 'action' => 'login'])
]);
// Load identifiers
$service->loadIdentifier('Authentication.Password', compact('fields'));
return $service;
}
AuthenticationService erstellen
Hierbei wird der AuthenticationService erstellt und konfiguriert. Die Option unauthenticatedRedirect
gibt an, wohin nicht authentifizierte Nutzer weitergeleitet werden sollen. Weitere Konfigurationsmöglichkeiten unter Configuration.
Authenticator hinzufügen
Als nächstes fügen wir mit loadAuthenticator()
Authenticator hinzu. In diesem Beispiel wird formularbasierte
Authentifizierung (Form) und PHP Session genutzt. Session sollte immer zuerst geladen werden. Die Formularfelder werden in der Option fields
konfiguriert.
Identifier hinzufügen
Abschließdend wird mit loadIdentifier()
noch der Identifier geladen. Falls euer Datenmodel anders aussehen sollte, kann mit der Option resolver hier ein anderes gesetzt werden. Die Option passwordHasher
kann genutzt werden, um ältere Password Hashes durch neue zu ersetzen. Mehr dazu unter https://book.cakephp.org/authentication/2/en/identifiers.html#password
Controller Component hinzufügen
In unserem AppController laden wir noch die Authentication Component:
// src/Controller/AppController.php
public function initialize(): void
{
parent::initialize();
$this->loadComponent('RequestHandler');
$this->loadComponent('Flash');
$this->loadComponent('Authentication.Authentication');
}
User Login Action implementieren
Damit sich der Nutzer auch anmelden kann muss der Zugriff auf die login()
Action möglich sein. Dazu wird in der beforeFilter()
des UsersControllerder Zugriff mit allowUnauthenticated
erlaubt.
Die Login Action nutzt die AuthenticationComponent und erhält das Ergebnis des Anmeldeversuchs. Falls der Anmeldeversuch erfolgreich war, könnt ihr den Nutzer auf eine bestimmte Seite weiterleiten.
// src/Controller/UsersController.php
public function beforeFilter(EventInterface $event)
{
$this->Authentication->allowUnauthenticated(['login']);
parent::beforeFilter($event);
}
public function login()
{
$result = $this->Authentication->getResult();
// If the user is logged in send them away.
if ($result->isValid()) {
$target = $this->Authentication->getLoginRedirect() ?? '/';
return $this->redirect($target);
}
if ($this->request->is('post') && !$result->isValid()) {
$this->Flash->error('Invalid username or password');
}
}
Als nächstes brauchen wir ein Anmeldeformular für unsere login()
Action. Dazu nutzen wir den FormHelper:
// in templates/Users/login.php
<?= $this->Form->create() ?>
<?= $this->Form->control('email') ?>
<?= $this->Form->control('password') ?>
<?= $this->Form->button(__('Login')) ?>
<?= $this->Form->end() ?>
User Registration Action implementieren
Aktuell gibt es allerdings keinen Nutzer in unserer Datenbank mit einem entsprechenden Passwort Hash. Dazu ändern wir die setter Methode für unser Passwort in der User Entity so ab, dass sie das Passwort als Hash speichert:
// in src/Model/Entity/User.php
protected function _setPassword(string $plainPassword)
{
$hasher = new DefaultPasswordHasher();
return $hasher->hash($plainPassword);
}
Um neue Nutzer zu registrieren erstellen wir als nächstes eine register()
Action. In dieser wird eine neue Entity erstellt und das dabei eingegeben Passwort wird als Hash in der Datenbank gespeichert.
// in src/Controller/UsersController.php
public function register()
{
$user = $this->Users->newEmptyEntity();
if ($this->request->is('post')) {
$user = $this->Users->patchEntity($user, $this->request->getData());
if ($this->Users->save($user)) {
$this->Flash->success(__('Your registration was successful.'));
return $this->redirect('/');
}
$this->Flash->error(__('Your registration failed.'));
}
}
Für das entsprechende Formular kann einfach der Inhalt der templates/Users/login.php
in das Template der register
Action nach templates/Users/register.php
kopiert werden.
Über die URL entsprechende URL (in meinem Fall http://localhost/users/register) kann nun ein neuer Nutzer angelegt werden. Unter http://localhost/users/ solltet ihr diesen Nutzer mit dem gehashten Passwort sehen.
Den kompletten Quellcode findet ihr auf Github.
Wir freuen uns über Interessierte