Autenticación
Nette ofrece una forma de implementar la autenticación en nuestros sitios, pero no impone ningún enfoque concreto. La
implementación depende por completo de nosotros. Nette incluye la interfaz Nette\Security\Authenticator, que exige
un único método, authenticate, para verificar al usuario de la forma que elijamos.
Hay muchas maneras de autenticar a un usuario. La más habitual es la autenticación por contraseña (donde el usuario indica su nombre o su correo y una contraseña), pero existen otras. Puede que conozca los botones “Iniciar sesión con Facebook” o el acceso mediante Google/Twitter/GitHub en algunos sitios. Con Nette podemos tener cualquier método de acceso, o incluso combinarlos. La decisión es enteramente nuestra.
Normalmente escribiríamos nuestro propio autenticador. Pero, para este blog sencillo, usaremos el autenticador integrado que
autentica a partir de un nombre de usuario y una contraseña guardados en el archivo de configuración. Es adecuado para hacer
pruebas. Añadamos la siguiente sección security al archivo de configuración config/common.neon:
security:
users:
admin: secret # usuario 'admin', contraseña 'secret'
Nette crea automáticamente un servicio para ese autenticador en el contenedor DI.
Formulario de acceso
Ahora que la autenticación está configurada, tenemos que crear una interfaz de usuario para iniciar sesión. Creemos un
presenter nuevo llamado SignPresenter que:
- muestre un formulario de acceso (con campos de nombre de usuario y contraseña)
- autentique al usuario al enviar el formulario
- ofrezca una opción para cerrar la sesión
Empezaremos por el formulario de acceso. Ya sabemos cómo funcionan los formularios en los presenters. Creemos el
SignPresenter y escribamos el método createComponentSignInForm. Debería quedar más
o menos así:
<?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', '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;
}
}
Hay campos para el nombre de usuario y la contraseña.
Plantilla
El formulario se renderizará en la plantilla in.latte:
{block content}
<h1 n:block=title>Sign in</h1>
{control signInForm}
Callback de acceso
A continuación añadiremos el método callback para autenticar al usuario, que se llamará justo después de enviar el formulario correctamente.
El callback simplemente toma el nombre de usuario y la contraseña introducidos por el usuario y se los pasa al autenticador. Tras un acceso correcto redirigimos a la portada.
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.');
}
}
El método User::login() lanza una excepción si el nombre de usuario y la contraseña no coinciden con las credenciales del archivo de configuración. Como sabemos, eso podría acabar en una pantalla roja de Tracy o, en modo de producción, en un mensaje de error del servidor. No queremos eso. Por eso capturamos la excepción y añadimos al formulario un bonito mensaje de error amable para el usuario.
Cuando se produce un error del formulario, la página con el formulario se vuelve a renderizar y sobre él aparece un mensaje que informa al usuario de que ha introducido un nombre de usuario o una contraseña incorrectos.
Proteger los presenters
Protegeremos el formulario para añadir y editar artículos, que está definido en EditPresenter. El objetivo es
impedir que los usuarios que no han iniciado sesión accedan a la página.
Crearemos un método startup(), que se ejecuta justo al principio del ciclo de vida del presenter. Este
método redirigirá al formulario de acceso a los usuarios que no hayan iniciado sesión.
public function startup(): void
{
parent::startup();
if (!$this->getUser()->isLoggedIn()) {
$this->redirect('Sign:in');
}
}
Ocultar los enlaces
Un usuario no autorizado ya no puede ver las páginas create ni edit, pero todavía ve los enlaces que apuntan a
ellas. También deberíamos ocultarlos. Uno de esos enlaces está en la plantilla app/Presentation/Home/default.latte
y solo debería ser visible para los usuarios que han iniciado sesión.
Podemos ocultarlo con un n:atributo llamado n:if. Si la condición es false, no se mostrará
toda la etiqueta <a>, incluido su contenido.
<a n:href="Edit:create" n:if="$user->isLoggedIn()">Create post</a>
que es un atajo de la siguiente notación (no la confunda con tag-if):
{if $user->isLoggedIn()}<a n:href="Edit:create">Create post</a>{/if}
Oculte de la misma manera el enlace de edición de la plantilla app/Presentation/Post/show.latte.
Enlace de acceso
¿Cómo llegamos en realidad a la página de acceso? No hay ningún enlace que lleve a ella. Añadamos uno a la plantilla
@layout.latte. Intente encontrar un sitio adecuado; puede ser casi cualquiera.
...
<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>
...
Si el usuario no ha iniciado sesión, se muestra el enlace “Sign in”. En caso contrario se muestra el enlace “Sign
out”. Añadiremos también esa acción a SignPresenter.
Como redirigimos al usuario justo después de cerrar la sesión, no hace falta ninguna plantilla. La acción de cierre de sesión tiene este aspecto:
public function actionOut(): void
{
$this->getUser()->logout();
$this->flashMessage('You have been signed out.');
$this->redirect('Home:');
}
Simplemente llama al método logout() y después muestra un mensaje de confirmación al usuario.
Resumen
Tenemos enlaces para iniciar y cerrar la sesión del usuario. Para la autenticación hemos usado el autenticador integrado y las credenciales de acceso están en el archivo de configuración, porque se trata de una aplicación de prueba sencilla. También hemos protegido los formularios de edición para que solo los usuarios conectados puedan añadir y editar artículos.
Aquí puede leer más sobre el acceso de usuarios y la autorización.