[英]FOSUserBundle - Force password change after first login
In a Symfony2 application using FOSUserBundle for user management, the user table has been filled through an import script from a csv file and the password generated from a combination of data.在使用 FOSUserBundle 进行用户管理的 Symfony2 应用程序中,用户表已通过从 csv 文件的导入脚本和从数据组合生成的密码进行填充。
I would like to force the user to change his password at the first login.我想强制用户在第一次登录时更改他的密码。
When the event FOSUserEvents::SECURITY_IMPLICIT_LOGIN
occurs, redirect to the route fos_user_change_password
if the field last_login
is NULL.当事件
FOSUserEvents::SECURITY_IMPLICIT_LOGIN
发生时,如果字段last_login
为 NULL,则重定向到路由fos_user_change_password
。
My idea was rewriting the method onImplicitLogin(UserEvent $event)
of the class AGI\\UserBundle\\EventListener\\LastLoginListener
like this but the class is not overwritten:我的想法是像这样重写
AGI\\UserBundle\\EventListener\\LastLoginListener
类的onImplicitLogin(UserEvent $event)
,但该类没有被覆盖:
public function onImplicitLogin(UserEvent $event) {
$user = $event->getUser ();
if ($user->getLastLogin () === null) {
$user->setLastLogin ( new \DateTime () );
$this->userManager->updateUser ( $user );
$response = new RedirectResponse ( $this->router->generate ( 'fos_user_change_password' ) );
$this->session->getFlashBag ()->add ( 'notice', 'Please change your password' );
$event->setResponse ( $response );
}
}
I already have a bundle overwriting FOSUserBundle and it works for controllers, forms, etc but It looks like it is not the way to do it with eventListeners.我已经有一个覆盖 FOSUserBundle 的包,它适用于控制器、表单等,但看起来它不是使用 eventListeners 的方法。
How can I force the user to change the password after the first login?如何强制用户在第一次登录后更改密码?
With the help of the precious hint from @sjagr about fos_user.security.implicit_login
that drove me to fos_user.security.implicit_login
and an external topic about doing stuff right after login , I got a working solution.在@sjagr 关于
fos_user.security.implicit_login
的宝贵提示的帮助下,我找到了fos_user.security.implicit_login
和一个关于登录后立即做事的外部主题,我得到了一个fos_user.security.implicit_login
解决方案。
AGI\\UserBundle\\Resources\\config\\services.yml AGI\\UserBundle\\Resources\\config\\services.yml
login_listener:
class: 'AGI\UserBundle\EventListener\LoginListener'
arguments: ['@security.context', '@router', '@event_dispatcher']
tags:
- { name: 'kernel.event_listener', event: 'security.interactive_login', method: onSecurityInteractiveLogin }
AGI\\UserBundle\\EventListener\\LoginListener.php AGI\\UserBundle\\EventListener\\LoginListener.php
<?php
namespace AGI\UserBundle\EventListener;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class LoginListener {
private $securityContext;
private $router;
private $dispatcher;
public function __construct(SecurityContext $securityContext, Router $router, EventDispatcherInterface $dispatcher) {
$this->securityContext = $securityContext;
$this->router = $router;
$this->dispatcher = $dispatcher;
}
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event) {
if ($this->securityContext->isGranted ( 'IS_AUTHENTICATED_FULLY' )) {
$user = $event->getAuthenticationToken ()->getUser ();
if ($user->getLastLogin () === null) {
$this->dispatcher->addListener ( KernelEvents::RESPONSE, array (
$this,
'onKernelResponse'
) );
}
}
}
public function onKernelResponse(FilterResponseEvent $event) {
$response = new RedirectResponse ( $this->router->generate ( 'fos_user_change_password' ) );
$event->setResponse ( $response );
}
}
Thank you谢谢
If you require user change password due to some business rules, you can use kernel request EventListener:如果由于某些业务规则需要用户更改密码,可以使用内核请求 EventListener:
<?php
namespace App\EventListener;
use App\Model\UserInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class ChangePasswordListener
{
private TokenStorageInterface $security;
private RouterInterface $router;
private array $excludedRoutes = [
'admin_change_password',
'admin_login',
'admin_login_check',
'admin_logout',
];
public function __construct(
TokenStorageInterface $security,
RouterInterface $router
) {
$this->security = $security;
$this->router = $router;
}
public function onKernelRequest(RequestEvent $event): void
{
if (false === $event->isMasterRequest()) {
return;
}
if ($event->getRequest()->isXmlHttpRequest()) {
return;
}
$currentRoute = $event->getRequest()->get('_route');
if (\in_array($currentRoute, $this->excludedRoutes, true)) {
return;
}
$token = $this->security->getToken();
if (null === $token) {
return;
}
$user = $token->getUser();
if ($user instanceof UserInterface && $user->shouldPasswordChange()) {
$response = new RedirectResponse($this->router->generate('admin_security_profile_change_password'));
$event->setResponse($response);
}
}
}
services.yaml:服务.yaml:
services:
App\EventListener\ChangePasswordListener:
arguments:
- '@security.token_storage'
- '@router'
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: -100 }
You should provide also own UserInterface with method "shouldPasswordChange" and custom implementation of it.您还应该提供自己的 UserInterface 方法“shouldPasswordChange”和它的自定义实现。
It works great with Symfony 5.0 and PHP 7.4 but if you modify this code it should works also for lower PHP versions.它适用于 Symfony 5.0 和 PHP 7.4,但如果您修改此代码,它也适用于较低的 PHP 版本。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.