Autenticazione
Nette fornisce le linee guida su come programmare l'autenticazione nella propria pagina, ma non obbliga a farlo in un modo
particolare. L'implementazione dipende da voi. Nette ha un'interfaccia Nette\Security\Authenticator
che obbliga a
implementare un solo metodo, chiamato authenticate
, che trova l'utente in qualsiasi modo.
Ci sono molti modi in cui un utente può autenticarsi. Il modo più comune è l'autenticazione basata su password (l'utente fornisce il suo nome o la sua e-mail e una password), ma ci sono anche altri modi. Forse conoscete i pulsanti “Accedi con Facebook” su molti siti web, o il login tramite Google/Twitter/GitHub o qualsiasi altro sito. Con Nette, potete scegliere qualsiasi metodo di autenticazione, oppure combinarli. Sta a voi decidere.
Normalmente si dovrebbe scrivere il proprio autenticatore, ma per questo semplice blog useremo l'autenticatore integrato, che
si autentica in base a una password e a un nome utente memorizzati in un file di configurazione. È ottimo per i test.
Aggiungeremo la seguente sezione security al file di configurazione config/common.neon
:
security:
users:
admin: secret # utente 'admin', password 'secret'
Nette creerà automaticamente un servizio nel contenitore DI.
Modulo di accesso
Ora abbiamo la parte di backend dell'autenticazione pronta e dobbiamo fornire un'interfaccia utente, attraverso la quale l'utente possa effettuare il login. Creiamo un nuovo presentatore, chiamato SignPresenter, che
- visualizzerà un modulo di login (chiedendo nome utente e password)
- autenticare l'utente quando il modulo viene inviato
- fornire un'azione di logout
Cominciamo con il modulo di accesso. Si sa già come funzionano i moduli in un presentatore. Creare il metodo
SignPresenter
e il metodo createComponentSignInForm
. Dovrebbe avere questo aspetto:
<?php
namespace App\UI\Sign;
use Nette;
use Nette\Application\UI\Form;
final class SignPresenter extends Nette\Application\UI\Presenter
{
protected function createComponentSignInForm(): Form
{
$form = new Form;
$form->addText('username', 'Username:')
->setRequired('Please enter your username.');
$form->addPassword('password', 'Password:')
->setRequired('Please enter your password.');
$form->addSubmit('send', 'Sign in');
$form->onSuccess[] = $this->signInFormSucceeded(...);
return $form;
}
}
C'è un input per il nome utente e la password.
Modello
Il modulo sarà reso nel template in.latte
{block content}
<h1 n:block=title>Sign in</h1>
{control signInForm}
Gestore dell'accesso
Aggiungiamo anche un gestore del modulo per l'accesso dell'utente, che viene invocato subito dopo l'invio del modulo.
Il gestore prenderà il nome utente e la password inseriti dall'utente e li passerà all'autenticatore definito in precedenza. Dopo che l'utente si è loggato, lo reindirizziamo alla homepage.
private function signInFormSucceeded(Form $form, \stdClass $data): void
{
try {
$this->getUser()->login($data->username, $data->password);
$this->redirect('Home:');
} catch (Nette\Security\AuthenticationException $e) {
$form->addError('Incorrect username or password.');
}
}
Il metodo User::login() dovrebbe lanciare un'eccezione quando il nome utente o la password non corrispondono a quelli definiti in precedenza. Come già sappiamo, ciò si tradurrebbe in una schermata rossa di Tracy o, in modalità di produzione, in un messaggio che informa di un errore interno del server. Non ci piacerebbe. Ecco perché catturiamo l'eccezione e aggiungiamo un messaggio di errore simpatico e amichevole al modulo.
Quando si verifica un errore nel modulo, la pagina con il modulo viene resa di nuovo e sopra il modulo viene visualizzato un messaggio simpatico, che informa l'utente che ha inserito un nome utente o una password errati.
Sicurezza dei presentatori
Verrà messo in sicurezza un modulo per l'aggiunta e la modifica dei messaggi. È definito nel presentatore
EditPresenter
. L'obiettivo è impedire l'accesso alla pagina agli utenti che non hanno effettuato il login.
Creiamo un metodo startup()
che viene avviato immediatamente all'inizio del ciclo di vita del presentatore. Questo
metodo reindirizza gli utenti non loggati al modulo di login.
public function startup(): void
{
parent::startup();
if (!$this->getUser()->isLoggedIn()) {
$this->redirect('Sign:in');
}
}
Nascondi link
Un utente non autenticato non può più vedere la pagina create né la pagina edit, ma può ancora vedere
i collegamenti che puntano a esse. Nascondiamo anche quelli. Uno di questi collegamenti si trova in
app/UI/Home/default.latte
, e dovrebbe essere visibile solo se l'utente è loggato.
Possiamo nasconderlo usando un n:attributo chiamato n:if
. Se l'istruzione al suo interno è
false
, l'intero tag <a>
e il suo contenuto non saranno visualizzati:
<a n:href="Edit:create" n:if="$user->isLoggedIn()">Create post</a>
questa è una scorciatoia per (non confonderla con tag-if
):
{if $user->isLoggedIn()}<a n:href="Edit:create">Create post</a>{/if}
Si dovrebbe nascondere il link di modifica situato in app/UI/Post/show.latte
in modo simile.
Collegamento al modulo di accesso
Ehi, ma come si arriva alla pagina di login? Non c'è nessun link che punta ad essa. Aggiungiamone uno nel file del template
@layout.latte
. Prova a trovare un posto carino, può essere ovunque ti piaccia di più.
...
<ul class="navig">
<li><a n:href="Home:">Home</a></li>
{if $user->isLoggedIn()}
<li><a n:href="Sign:out">Sign out</a></li>
{else}
<li><a n:href="Sign:in">Sign in</a></li>
{/if}
</ul>
...
Se l'utente non ha ancora effettuato il login, mostreremo il link “Accedi”. Altrimenti, mostreremo il link “Esci”. Aggiungiamo questa azione in SignPresenter.
L'azione di logout ha questo aspetto e, poiché reindirizziamo l'utente immediatamente, non è necessario un modello di vista.
public function actionOut(): void
{
$this->getUser()->logout();
$this->flashMessage('You have been signed out.');
$this->redirect('Home:');
}
Richiama semplicemente il metodo logout()
e mostra un bel messaggio all'utente.
Riepilogo
Abbiamo un link per il login e anche per il logout dell'utente. Abbiamo usato l'autenticatore integrato per l'autenticazione e i dettagli di login sono nel file di configurazione, poiché si tratta di una semplice applicazione di prova. Abbiamo anche protetto i moduli di modifica in modo che solo gli utenti registrati possano aggiungere e modificare i post.
Qui si possono leggere ulteriori informazioni su login e autorizzazione degli utenti.