¡Hola de nuevo! Esto es un codekata, esta vez para páginas web construidas con Symfony, con registro, autenticación y autorización de usuarios. Se trata de un clásico para generar toda la estructura necesaria para crear desde cero un proyecto funcional en unos minutos.
Hasta la versión 4 de Symfony teníamos vendors, como FOSUserBundle, con lo que estas cosas de registros de usuarios venían hechas. Ahora a partir de la versión 5 de Symfony también, tenemos de casa componentes para integrar estas cosas, generando códigos y configuraciones en pocos minutos.
Vamos al grano y lo vemos?¿ ??
Para seguir el codekata te invito a que generes desde cero el proyecto en tu escritorio. Si todo sale bien, hará falta menos de una hora para tenerlo funcionando. Los pasos que vamos a seguir son:
- Generar el proyecto.
- Requerir con Composer los paquetes necesarios.
- Configurar un controlador por defecto.
- Configurar una entidad para autenticar con usuarios almacenados en BD.
- Haremos un login funcional.
- Crearemos un formulario de registro, con validación por email del nuevo usuario.
- Crearemos un formulario de recuperación de contraseña.
- Incluiremos Bootstrap 5 para convertir en responsive el nuevo proyecto.
Al grano con el codekata, empezamos a crear el proyecto
Lanzamos el comando de Symfony desde un terminal:
symfony new symfony-testing-security
..y se creará el proyecto en la carpeta en la que estemos. Si no tenemos el comando de Symfony, Composer y las herramientas adecuadas me remito a un post anterior sobre cómo preparar el entorno de trabajo para Symfony.
Requerir con Composer los paquetes necesarios.
Lo siguiente es instalar los paquetes que vamos a usar. Para el entorno de desarrollo sólo el maker (que nos generará los códigos fuentes), y profiler (que nos dará una barra de tareas para ver el usuario si ha hecho login), veo necesarios para este codekata:
composer require --dev maker profiler
Para el entorno de producción necesitaremos los siguientes:
composer require maker orm twig form validator symfonycasts/verify-email-bundle symfony/mailer symfonycasts/reset-password-bundle
Si no queremos instalarlos ahora, o no los recordamos, no pasa nada. A medida que vayamos generando los códigos fuentes, la misma consola de Symfony nos explicará si necesitamos instalar algo más según lo que queremos construir.
Configurar un controlador por defecto
Lo ideal para tener algo que ver en el navegador es tener alguna página. Con este comando nos podemos crear un controlador, con una página de plantilla Twig:
php bin/console make:controller DefaultController
Le podemos llamar por ejemplo DefaultController, se verá así en el navegador si lo arrancamos en local:
http://localhost:8000/default
..al llamarlo DefaultController, el maker de Symfony enlazará la ruta /default con la función generada en el fichero:
src/Controller/DefaultController.php
Configurar una entidad para autenticar con usuarios almacenados en BD
Lo siguiente es que los usuarios los querremos almacenar en BD, con lo que con Doctrine y la consola de Symfony podemos generar la entidad PDO de usuario. Esta entidad, o también llamada PDO, simplemente es una clase de PHP que nos va a mapear los campos de la BD, con atributos y métodos para manejar los datos de los usuarios.
Con el siguiente comando se puede generar automáticamente el código:
php bin/console make:user
Hacemos un login funcional
Lo siguiente que querremos probablemente es generar el código para hacer un login de usuarios. Para esto también tenemos en Symfony un generador de códigos fuentes con el que tendremos un buen punto de partida. Se puede lanzar así:
php bin/console make:auth
..y seguiendo las instrucciones lo tendremos funcionando. Nos generará un controlador y una clase que hace la autenticación.
Creamos un formulario de registro, con validación por email del nuevo usuario
Ya hemos generado en el apartado anterior el formulario de login. Pero para poder utilizarlo, necesitaremos tener usuarios registrados. Este proceso de registro podemos también generarlo con el siguiente comando:
php bin/console make:registration-form
Lo que tendremos con este generador es un formulario de registro que trabaja sobre el PDO anterior, haciendo un registro y almacenando en BD. Siguiendo las instrucciones tendremos más información, pudiendo verificar el email de registro, enviando un email al usuario para verificar que existe el email, etc..
Creamos un formulario de recuperación de contraseña
Otra funcionalidad que también querremos es la de resetear la contraseña. También tenemos otro generador de código fuente en Symfony que hace esto. Podemos lanzar este comando:
php bin/console make:reset-password
..siguiendo las instrucciones podremos elegir algunas configuraciones y nos generará todos los códigos fuentes de la imagen siguiente:
Incluimos Bootstrap 5 para convertir todo en responsivo
Para ir terminando con este codekata, sólo nos queda ponerlo bonito. Si usamos Bootstrap, también tenemos ayuda en Symfony, con lo que podemos consultar esta página oficial de la documentación:
https://symfony.com/doc/current/form/bootstrap5.html
Si incluimos en el fichero de configuración de Twig esta plantilla:
form_themes: ['bootstrap_5_layout.html.twig']
..está en config/packages/twig.yaml. Tendremos entonces que los formularios se generarán usando las clases CSS y etiquetas HTML que usa Bootstrap.
Nos quedará entonces cargar las librerías CSS y JS para que funcione en el HEAD. Podríamos hacerlo por ejemplo editando el fichero templates/base.html.twig con algo parecido a lo siguiente:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
{# Run `composer require symfony/webpack-encore-bundle`
and uncomment the following Encore helpers to start using Symfony UX #}
{% block stylesheets %}
{#{{ encore_entry_link_tags('app') }}#}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
{% endblock %}
{% block javascripts %}
{#{{ encore_entry_script_tags('app') }}#}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
{% endblock %}
</head>
<body class="container">
{% block body %}{% endblock %}
</body>
</html>
Arrancamos el proyecto para probarlo en local
Si has seguido el codekata, sólo queda configurar en local la BD y servidor de emails en el fichero .env, crear la BD, el esquema y arrancar el servidor. Se puede hacer lanzando estos comandos después de configurar el .env:
php bin/console doctrine:database:create
php bin/console doctrine:schema:create
symfony server:start
..y si todo ha ido bien, en local tendremos en http://localhost:8000/register el inicio del proceso para probarlo. Se tendría que ver algo parecido a lo siguiente:
Después de hacer un registro de pruebas, tenemos que poder entrar con dicho email y contraseña que hayamos puesto en http://localhost:8000/login. Con lo que si todo ha ido bien se tiene que ver algo como lo siguiente en local después de haber hecho login:
La autorización para acceder a ciertos contenidos
Con lo anterior, ya tenemos todo el tema de la autenticación, es decir, ya sabemos que el usuario es quien dice ser. Ahora sólo quedaría para terminar con el tema de la autorización de acceso, es decir, el permitir a cierto tipo de usuarios el acceso a ciertos contenidos.
Para terminar entonces con el codekata, una forma sería configurar la ruta /default para acceso sólo a usuarios registrados. Esto se puede conseguir añadiendo en el fichero config/packages/security.yaml algo como lo siguiente:
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
# - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }
- { path: /default, roles: ROLE_USER }
Esto provocará que la aplicación requiera el ROLE_USER en dicho directorio http://localhost:8000/default. Lo que hará que si accedemos sin haber hecho login, nos lleve al login, y si ponemos usuario y contraseña correctos, podremos ver la página /default.