gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
Independientemente de cómo se autentifiquen tus usuarios -un formulario de inicio de sesión, una autenticación social o una clave de la API-, tu sistema de seguridad necesita algún concepto de usuario: alguna clase que describa la "cosa" que ha iniciado la sesión.
Sí, el paso 1 de la autenticación es crear una clase User
. ¡Y hay un comando que puede ayudarnos! Busca tu terminal y ejecuta:
symfony console make:user
Como recordatorio, symfony console
es sólo un atajo para bin/console
... pero como estoy usando la integración de Docker con el servidor web Symfony, llamar a symfony console
permite al binario symfony
inyectar algunas variables de entorno que apuntan a la base de datos de Docker. No importará para este comando, pero sí para cualquier comando que hable con la base de datos.
Bien, primera pregunta:
El nombre de la clase de usuario
Normalmente, será User
... aunque sería mejor utilizar algo comoHumanoidEntity
. Si la "cosa" que entra en tu sitio se llamaría mejor Company
o University
o Machine
, utiliza ese nombre aquí.
¿Quieres almacenar los datos de los usuarios en la base de datos a través de Doctrine?
Para nosotros: es un sí rotundo... pero no es un requisito. Tus datos de usuario pueden estar almacenados en algún otro servidor... aunque incluso en ese caso, a menudo es conveniente almacenar algunos datos adicionales en tu base de datos local... en cuyo caso también dirías que sí aquí.
Siguiente:
Introduce un nombre de propiedad que será el nombre de visualización único para el usuario.
Yo voy a utilizar email
. Esto no es tan importante, y explicaré cómo se utiliza en unos minutos. Por último:
¿Necesitará esta aplicación hacer un hash y comprobar las contraseñas de los usuarios?
Sólo tienes que decir que sí si será responsabilidad de tu aplicación comprobar la contraseña del usuario cuando se conecte. Vamos a hacer esto... pero voy a decir que no. Lo añadiremos manualmente un poco más tarde.
Pulsa enter y... ¡listo!
Bien. ¿Qué ha hecho esto? En primer lugar, ha creado una entidad User
y una UserRepository
... exactamente lo mismo que se obtiene normalmente al ejecutar make:entity
. Vamos a ver esa nueva clase User
: src/Entity/User.php
:
... lines 1 - 2 | |
namespace App\Entity; | |
use App\Repository\UserRepository; | |
use Doctrine\ORM\Mapping as ORM; | |
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; | |
use Symfony\Component\Security\Core\User\UserInterface; | |
/** | |
* @ORM\Entity(repositoryClass=UserRepository::class) | |
*/ | |
class User implements UserInterface | |
{ | |
/** | |
* @ORM\Id | |
* @ORM\GeneratedValue | |
* @ORM\Column(type="integer") | |
*/ | |
private $id; | |
/** | |
* @ORM\Column(type="string", length=180, unique=true) | |
*/ | |
private $email; | |
/** | |
* @ORM\Column(type="json") | |
*/ | |
private $roles = []; | |
... lines 31 - 113 | |
} |
En primer lugar, se trata de una entidad normal y aburrida de Doctrine: tiene anotaciones -o quizás atributos de PHP 8 para ti- y un id. Es... sólo una entidad: no tiene nada de especial.
Lo único que le importa a Symfony es que tu clase de usuario implementeUserInterface
. Mantén pulsado Command
o Ctrl
y haz clic para saltar al código del núcleo para ver esto.
Esta interfaz realmente sólo tiene 3 métodos: getUserIdentifier()
el que ves documentado encima de la interfaz, getRoles()
... y otro más abajo llamado eraseCredentials()
. Si estás confundido sobre por qué estoy omitiendo todos estos otros métodos, es porque están obsoletos. En Symfony 6, esta interfaz sólo tendrá esos 3: getUserIdentifier()
, getRoles()
yeraseCredentials()
.
En nuestra clase User
, si te desplazas hacia abajo, el comando make:user
implementó todo esto por nosotros. Gracias a cómo hemos respondido a una de sus preguntas,getUserIdentier()
devuelve el correo electrónico:
... lines 1 - 12 | |
class User implements UserInterface | |
{ | |
... lines 15 - 48 | |
/** | |
* A visual identifier that represents this user. | |
* | |
* @see UserInterface | |
*/ | |
public function getUserIdentifier(): string | |
{ | |
return (string) $this->email; | |
} | |
... lines 58 - 113 | |
} |
Esto... no es demasiado importante: es sobre todo una representación visual de tu objeto Usuario... se utiliza en la barra de herramientas de depuración de la web... y en algunos sistemas opcionales, como el sistema "recuérdame".
Si estás usando Symfony 5 como yo, te darás cuenta de que los métodos obsoletos se siguen generando. Son necesarios sólo por compatibilidad con versiones anteriores, y puedes eliminarlos una vez que estés en Symfony 6.
El método getRoles()
se ocupa de los permisos:
... lines 1 - 12 | |
class User implements UserInterface | |
{ | |
... lines 15 - 66 | |
/** | |
* @see UserInterface | |
*/ | |
public function getRoles(): array | |
{ | |
$roles = $this->roles; | |
// guarantee every user at least has ROLE_USER | |
$roles[] = 'ROLE_USER'; | |
return array_unique($roles); | |
} | |
... lines 78 - 113 | |
} |
más adelante se hablará de ello. Además, getPassword()
y getSalt()
están obsoletos:
... lines 1 - 12 | |
class User implements UserInterface | |
{ | |
... lines 15 - 85 | |
/** | |
* This method can be removed in Symfony 6.0 - is not needed for apps that do not check user passwords. | |
* | |
* @see PasswordAuthenticatedUserInterface | |
*/ | |
public function getPassword(): ?string | |
{ | |
return null; | |
} | |
/** | |
* This method can be removed in Symfony 6.0 - is not needed for apps that do not check user passwords. | |
* | |
* @see UserInterface | |
*/ | |
public function getSalt(): ?string | |
{ | |
return null; | |
} | |
... lines 105 - 113 | |
} |
Seguirás necesitando el método getPassword()
si compruebas las contraseñas en tu sitio, pero ya lo veremos más adelante. Por último, eraseCredentials()
forma parte deUserInterface
:
... lines 1 - 12 | |
class User implements UserInterface | |
{ | |
... lines 15 - 105 | |
/** | |
* @see UserInterface | |
*/ | |
public function eraseCredentials() | |
{ | |
// If you store any temporary, sensitive data on the user, clear it here | |
// $this->plainPassword = null; | |
} | |
} |
pero no es muy importante y también hablaremos de ello más adelante.
Así que a alto nivel... si ignoras los métodos obsoletos... y el no tan importanteeraseCredentials()
, lo único que debe tener nuestra clase User
es un identificador y un método que devuelva la matriz de roles que debe tener este usuario. Sí... es sobre todo una entidad de User
.
El comando make:user
también hizo un ajuste en nuestro archivo security.yaml
: puedes verlo aquí:
security: | |
... lines 2 - 7 | |
providers: | |
# used to reload user from session & other features (e.g. switch_user) | |
app_user_provider: | |
entity: | |
class: App\Entity\User | |
property: email | |
... lines 14 - 33 |
Añadió lo que se llama un "proveedor de usuario", que es un objeto que sabe cómo cargar tus objetos de usuario... ya sea que cargues esos datos desde una API o desde una base de datos. Como estamos usando Doctrine, podemos usar el proveedor incorporado entity
: sabe cómo obtener nuestros usuarios de la base de datos usando la propiedad email
.
Quería que vieras este cambio... pero el proveedor de usuarios no es importante todavía. Te mostraré exactamente cómo y dónde se utiliza a medida que avancemos.
A continuación: tenemos un control total sobre el aspecto de nuestra clase User
. ¡El poder! Así que vamos a añadirle un campo personalizado y a cargar nuestra base de datos con un buen conjunto de usuarios ficticios.
// composer.json
{
"require": {
"php": "^7.4.1 || ^8.0.0",
"ext-ctype": "*",
"ext-iconv": "*",
"babdev/pagerfanta-bundle": "^3.3", // v3.3.0
"composer/package-versions-deprecated": "^1.11", // 1.11.99.4
"doctrine/annotations": "^1.0", // 1.13.2
"doctrine/doctrine-bundle": "^2.1", // 2.6.3
"doctrine/doctrine-migrations-bundle": "^3.0", // 3.1.1
"doctrine/orm": "^2.7", // 2.10.1
"knplabs/knp-markdown-bundle": "^1.8", // 1.9.0
"knplabs/knp-time-bundle": "^1.11", // v1.16.1
"pagerfanta/doctrine-orm-adapter": "^3.3", // v3.3.0
"pagerfanta/twig": "^3.3", // v3.3.0
"phpdocumentor/reflection-docblock": "^5.2", // 5.2.2
"scheb/2fa-bundle": "^5.12", // v5.12.1
"scheb/2fa-qr-code": "^5.12", // v5.12.1
"scheb/2fa-totp": "^5.12", // v5.12.1
"sensio/framework-extra-bundle": "^6.0", // v6.2.0
"stof/doctrine-extensions-bundle": "^1.4", // v1.6.0
"symfony/asset": "5.3.*", // v5.3.4
"symfony/console": "5.3.*", // v5.3.7
"symfony/dotenv": "5.3.*", // v5.3.8
"symfony/flex": "^1.3.1", // v1.17.5
"symfony/form": "5.3.*", // v5.3.8
"symfony/framework-bundle": "5.3.*", // v5.3.8
"symfony/monolog-bundle": "^3.0", // v3.7.0
"symfony/property-access": "5.3.*", // v5.3.8
"symfony/property-info": "5.3.*", // v5.3.8
"symfony/rate-limiter": "5.3.*", // v5.3.4
"symfony/runtime": "5.3.*", // v5.3.4
"symfony/security-bundle": "5.3.*", // v5.3.8
"symfony/serializer": "5.3.*", // v5.3.8
"symfony/stopwatch": "5.3.*", // v5.3.4
"symfony/twig-bundle": "5.3.*", // v5.3.4
"symfony/ux-chartjs": "^1.3", // v1.3.0
"symfony/validator": "5.3.*", // v5.3.8
"symfony/webpack-encore-bundle": "^1.7", // v1.12.0
"symfony/yaml": "5.3.*", // v5.3.6
"symfonycasts/verify-email-bundle": "^1.5", // v1.5.0
"twig/extra-bundle": "^2.12|^3.0", // v3.3.3
"twig/string-extra": "^3.3", // v3.3.3
"twig/twig": "^2.12|^3.0" // v3.3.3
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.3", // 3.4.0
"symfony/debug-bundle": "5.3.*", // v5.3.4
"symfony/maker-bundle": "^1.15", // v1.34.0
"symfony/var-dumper": "5.3.*", // v5.3.8
"symfony/web-profiler-bundle": "5.3.*", // v5.3.8
"zenstruck/foundry": "^1.1" // v1.13.3
}
}