Autenticazione
Nette offre un modo per realizzare l'autenticazione sui nostri siti, ma non impone alcun approccio particolare. La
realizzazione dipende interamente da noi. Nette contiene l'interfaccia Nette\Security\Authenticator, che richiede un
solo metodo, authenticate, per verificare l'utente nel modo che preferiamo.
Ci sono molti modi di autenticare un utente. Il metodo più diffuso è l'autenticazione con password (l'utente indica il proprio nome o la propria email e una password), ma esistono anche altri metodi. Conoscete di certo i pulsanti “Accedi con Facebook” oppure l'accesso tramite Google/Twitter/GitHub su alcuni siti. Con Nette possiamo avere qualsiasi metodo di accesso, o anche combinarli. Dipende interamente da noi.
Di solito scriveremmo un autenticatore nostro. Per questo semplice blog useremo però l'autenticatore integrato, che fa
accedere in base a nome utente e password salvati nel file di configurazione. È adatto agli scopi di prova. Aggiungiamo al file
di configurazione config/common.neon questa sezione security:
security:
users:
admin: secret # utente 'admin', password 'secret'
Nette crea automaticamente nel container DI un servizio per questo autenticatore.
Form di accesso
Ora che l'autenticazione è impostata, dobbiamo creare un'interfaccia utente per l'accesso. Creiamo un nuovo presenter chiamato
SignPresenter, che:
- mostrerà il form di accesso (con i campi nome utente e password)
- autenticherà l'utente all'invio del form
- offrirà la possibilità di disconnettersi
Cominciamo dal form di accesso. Sappiamo già come funzionano i form nei presenter. Creiamo il SignPresenter e
scriviamo il metodo createComponentSignInForm. Dovrebbe apparire più o meno così:
<?php
namespace App\Presentation\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', 'Nome utente:')
->setRequired('Inserite il vostro nome utente.');
$form->addPassword('password', 'Password:')
->setRequired('Inserite la vostra password.');
$form->addSubmit('send', 'Accedi');
$form->onSuccess[] = $this->signInFormSucceeded(...);
return $form;
}
}
Ci sono i campi per il nome utente e la password.
Template
Il form verrà renderizzato nel template in.latte:
{block content}
<h1 n:block=title>Accedi</h1>
{control signInForm}
Callback di accesso
Aggiungiamo poi il metodo di callback per far accedere l'utente, che verrà chiamato subito dopo l'invio riuscito del form.
Il callback si limita a prendere il nome utente e la password inseriti dall'utente e a passarli all'autenticatore. Dopo l'accesso riuscito reindirizziamo alla pagina principale.
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('Nome utente o password non corretti.');
}
}
Il metodo User::login() lancia un'eccezione se il nome utente e la password non corrispondono alle credenziali nel file di configurazione. Come sappiamo, questo porterebbe alla schermata rossa di Tracy oppure, in modalità produzione, a un messaggio di errore del server. Non lo vogliamo. Catturiamo perciò l'eccezione e aggiungiamo al form un bel messaggio di errore comprensibile per l'utente.
Quando si verifica un errore del form, la pagina con il form viene renderizzata di nuovo e sopra il form compare un messaggio che informa l'utente di aver inserito un nome utente o una password sbagliati.
Mettere in sicurezza i presenter
Metteremo in sicurezza il form per aggiungere e modificare gli articoli, definito nell'EditPresenter. L'obiettivo
è impedire agli utenti non connessi di accedere alla pagina.
Creeremo il metodo startup(), che viene eseguito subito all'inizio del ciclo di vita del presenter. Questo
metodo reindirizzerà gli utenti non connessi al form di accesso.
public function startup(): void
{
parent::startup();
if (!$this->getUser()->isLoggedIn()) {
$this->redirect('Sign:in');
}
}
Nascondere i link
Un utente non autorizzato non può più vedere le pagine create e edit, ma vede ancora i link che vi puntano.
Dovremmo nascondere anche quelli. Uno di questi link è nel template app/Presentation/Home/default.latte e dovrebbe
essere visibile solo agli utenti connessi.
Lo possiamo nascondere con un n:attributo chiamato n:if. Se la condizione è false, l'intero
tag <a> con il suo contenuto non verrà mostrato.
<a n:href="Edit:create" n:if="$user->isLoggedIn()">Crea articolo</a>
che è una scorciatoia per questa notazione (non confondetela con tag-if):
{if $user->isLoggedIn()}<a n:href="Edit:create">Crea articolo</a>{/if}
Nascondete allo stesso modo il link di modifica nel template app/Presentation/Post/show.latte.
Link di accesso
Come si arriva davvero alla pagina di accesso? Non c'è nessun link che vi porti. Aggiungiamone uno al template
@layout.latte. Provate a trovare un posto adatto, può essere quasi ovunque.
...
<ul class="navig">
<li><a n:href="Home:">Home</a></li>
{if $user->isLoggedIn()}
<li><a n:href="Sign:out">Esci</a></li>
{else}
<li><a n:href="Sign:in">Accedi</a></li>
{/if}
</ul>
...
Se l'utente non è connesso, viene mostrato il link “Accedi”. Altrimenti viene mostrato il link “Esci”. Aggiungiamo
anche questa azione al SignPresenter.
Poiché dopo la disconnessione reindirizziamo subito l'utente, non serve alcun template. L'azione di disconnessione appare così:
public function actionOut(): void
{
$this->getUser()->logout();
$this->flashMessage('La disconnessione è avvenuta.');
$this->redirect('Home:');
}
Si limita a chiamare il metodo logout() e poi a mostrare all'utente un messaggio di conferma.
Riepilogo
Abbiamo i link per far accedere e disconnettere l'utente. Per l'autenticazione abbiamo usato l'autenticatore integrato e le credenziali di accesso stanno nel file di configurazione, perché si tratta di una semplice applicazione di prova. Abbiamo inoltre messo in sicurezza i form di modifica, così che solo gli utenti connessi possano aggiungere e modificare gli articoli.
Qui potete leggere di più sull'accesso degli utenti e sull'autorizzazione.