If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeSi has utilizado Symfony durante un tiempo, probablemente sabrás que Symfony envía eventos durante el proceso de petición-respuesta y que puedes escucharlos. Para ver estos eventos y sus oyentes, podemos ejecutar:
symfony console debug:event
No voy a profundizar demasiado, pero, este evento kernel.request
se despacha en cada petición antes de que se llame al controlador. Esto significa que todos estos oyentes se ejecutan antes que nuestro controlador. Los oyentes de este evento kernel.response
son llamados después de nuestro controlador.
Estos dos eventos no tienen... nada que ver con el sistema de seguridad. Pero resulta que nuestro cortafuegos también envía varios eventos durante el proceso de autenticación, y también podemos escucharlos.
Para ver una lista de todos los oyentes de estos eventos, podemos ejecutar de nuevo debug:event
, pero con un --dispatcher=
especial fijado en security.event_dispatcher.main
:
symfony console debug:event --dispatcher=security.event_dispatcher.main
Lo sé, parece un poco raro... pero esto nos permite listar los oyentes de eventos para el despachador de eventos que es específico del cortafuegos main
.
Y... ¡impresionante! Un conjunto totalmente diferente de eventos y escuchadores. Esto es genial. Vuelve a mirar nuestra clase personalizada LoginFormAuthenticator
. Ya no la utilizamos, pero puede ayudarnos a entender qué eventos se envían a través del proceso.
Sabemos que, en nuestro método authenticate()
, nuestro trabajo es devolver el Passport
:
... lines 1 - 26 | |
class LoginFormAuthenticator extends AbstractLoginFormAuthenticator | |
{ | |
... lines 29 - 39 | |
public function authenticate(Request $request): PassportInterface | |
{ | |
$email = $request->request->get('email'); | |
$password = $request->request->get('password'); | |
return new Passport( | |
new UserBadge($email, function($userIdentifier) { | |
// optionally pass a callback to load the User manually | |
$user = $this->userRepository->findOneBy(['email' => $userIdentifier]); | |
if (!$user) { | |
throw new UserNotFoundException(); | |
} | |
return $user; | |
}), | |
new PasswordCredentials($password), | |
[ | |
new CsrfTokenBadge( | |
'authenticate', | |
$request->request->get('_csrf_token') | |
), | |
(new RememberMeBadge())->enable(), | |
] | |
); | |
} | |
... lines 66 - 81 | |
} |
Entonces, después de llamar al método authenticate()
-en cualquier autentificador- Symfony despacha CheckPassportEvent
. Hay un montón de oyentes interesantes para esto.
Por ejemplo, UserProviderListener
es básicamente responsable de cargar el objeto User
, CheckCredentialsListener
es responsable de comprobar la contraseña,CsrfProtectionListener
valida el token CSRF y LoginThrottlingListener
comprueba... el estrangulamiento del inicio de sesión.
Si falla la autenticación, hay un evento diferente para eso: LoginFailureEvent
. Ahora mismo, nuestra aplicación sólo tiene un oyente - RememberMeListener
- que borra la cookie "recuérdame" si el usuario tenía una.
Cuando el inicio de sesión tiene éxito, Symfony envía LoginSuccessEvent
. Esto ya tiene 5 oyentes en nuestra aplicación, incluyendo el oyente que establece la cookie "recuérdame".
También hay un evento que se envía cuando se cierra la sesión... para que puedas ejecutar código o incluso controlar lo que ocurre, como a dónde se redirige al usuario.
El siguiente - TokenDeauthenticatedEvent
- es un poco más sutil. Se envía si el usuario "pierde" la autenticación... pero no se cierra la sesión. Básicamente se envía si cambian ciertos datos del usuario. Por ejemplo, imagina que estás conectado en dos ordenadores y luego cambias tu contraseña en el primero. Cuando actualices el sitio en el segundo ordenador, serás "desautenticado" porque tu contraseña ha cambiado en otra máquina. En ese caso, se envía este evento.
Ah, y este security.authentication.success
no es demasiado importante, es muy similar a LoginSuccessEvent
.
Conocer estos eventos es fundamental porque quiero hacer que si el usuario intenta iniciar sesión con un correo electrónico que no ha sido verificado, lo impidamos y le mostremos un bonito mensaje.
Para ello, vamos a poner en marcha nuestro propio receptor de eventos que tiene la capacidad de hacer que la autenticación falle.
Hey Trafficmanagertech
Inside your `security.yaml` file do you have a "main" key defined as your firewall?
Cheers!
// composer.json
{
"require": {
"php": ">=8.1",
"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
}
}
In SF 5.3, `symfony console debug:event --dispatcher=security.event_dispatcher.main` doesn't work anymore (got "[ERROR] Event dispatcher "security.event_dispatcher.main" is not available.")
In my case this worked: `symfony console debug:event-dispatcher security`