FOSUserBundle - 首次登录后强制更改密码


FOSUserBundle - Force password change after first login

在使用FOSUserBundle进行用户管理的Symfony2应用程序中,用户表是通过csv文件中的导入脚本和从数据组合生成的密码来填充的。

我想强制用户在第一次登录时更改密码。

发生FOSUserEvents::SECURITY_IMPLICIT_LOGIN事件时,如果字段last_login为 NULL,请重定向到路由fos_user_change_password

我的想法是像这样重写类的方法onImplicitLogin(UserEvent $event) AGI'UserBundle'EventListener'LastLoginListener,但类没有被覆盖:

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 );
    }
}

我已经有一个覆盖 FOSUserBundle 的捆绑包,它适用于控制器、表单等,但看起来这不是使用事件监听器的方法。

如何强制用户在首次登录后更改密码?

借助@sjagr关于驱使我进入fos_user.security.implicit_loginfos_user.security.implicit_login的宝贵提示以及有关登录后立即做事的外部主题,我得到了一个可行的解决方案。

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

<?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 );
    }
}

谢谢

如果由于某些业务规则需要用户更改密码,可以使用内核请求 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:

services:
    App'EventListener'ChangePasswordListener:
        arguments:
            - '@security.token_storage'
            - '@router'
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: -100 }

您还应该提供自己的用户界面,其中包含方法"shouldPasswordChange"及其自定义实现。

它适用于Symfony 5.0和PHP 7.4,但如果你修改此代码,它应该也适用于较低的PHP版本。