简体   繁体   English

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

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM