Nette Documentation Preview

syntax
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`:


```neon .{file:config/common.neon}
security:
	users:
		admin: secret # utente 'admin', password 'secret'
```

Nette creerà automaticamente un servizio nel contenitore DI.


Modulo di accesso .[#toc-sign-in-form]
======================================

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 .{file:app/UI/Sign/SignPresenter.php}
<?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 .[#toc-template]
------------------------

Il modulo sarà reso nel template `in.latte`

```latte .{file:app/UI/Sign/in.latte}
{block content}
<h1 n:block=title>Sign in</h1>

{control signInForm}
```


Gestore dell'accesso .[#toc-login-handler]
------------------------------------------

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.

```php .{file:app/UI/Sign/SignPresenter.php}
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() |api:Nette\Security\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 |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 .[#toc-security-of-presenters]
=========================================================

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 |application:presenters#life-cycle-of-presenter]. Questo metodo reindirizza gli utenti non loggati al modulo di login.

```php .{file:app/UI/Edit/EditPresenter.php}
public function startup(): void
{
	parent::startup();

	if (!$this->getUser()->isLoggedIn()) {
		$this->redirect('Sign:in');
	}
}
```


Nascondi link .[#toc-hide-links]
--------------------------------

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:

```latte
<a n:href="Edit:create" n:if="$user->isLoggedIn()">Create post</a>
```

questa è una scorciatoia per (non confonderla con `tag-if`):

```latte
{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 .[#toc-login-form-link]
=========================================================

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ù.

```latte .{file:app/UI/@layout.latte}
...
<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.

```php .{file:app/UI/Sign/SignPresenter.php}
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 .[#toc-summary]
=========================

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.

.[note]
Qui si possono leggere ulteriori informazioni su [login |security:authentication] e [autorizzazione |security:authorization] [degli utenti |security:authentication].

{{priority: -1}}
{{sitename: Nette Quickstart}}

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

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.

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.