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');
	}
}

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.

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.

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.