FOSUserBundle - 更改默认配置,覆盖某些默认设置

时间:2016-01-17 12:37:23

标签: authentication registration fosuserbundle

我是Symfony 2中的新手。我必须在Symfony 2中构建一个应用程序(我使用的是2.8以及最新版本的FOSUser Bundle)。大部分工作已完成,但我必须对默认安全设置进行一些更改。我正在寻找他们两天,我只有非常模糊的概念如何解决这个问题。假设的变化如下:

  • 注册 - 我已通过电子邮件启用确认,但我不知道在一小时后和/或一次使用后(一次点击)如何使确认链接过期。 config.yaml设置ttl中有设置,但仅用于密码重新设置。
  • 注册 - 在确认用户无法登录并且存在Symfony 2异常 - 禁用帐户工作之前,会发送有关它的短消息。实际上我必须将重定向设置为另一个页面(我猜模板)来呈现自定义消息,该帐户被阻止...'并链接以发送另一个带有确认令牌的链接(我是否正确?电子邮件中的链接是确认令牌?)。
  • 重置 - 如前所述ttl for link(令牌?)ressetting密码设置为一小时,但我不知道如何在一次使用(一次点击)后使其过期。

我知道如何覆盖FOSUser的某些模板,但我不知道应该覆盖哪些文件来更改这些内容。

我注意到我的FOSUser使用了Symfony 2 Exceptions文件,如果我在我的Exception文件中更改了邮件内容,它在我的页面上也发生了变化,但我不知道如何做好并覆盖它它,添加所有必要的功能。

我试图使用Compiler Pass(http://symfony.com/doc/current/cookbook/service_container/compiler_passes.html)覆盖AuthenticationListener(来自FOSUser),但我不知道它是否有效,因为覆盖Listener中的任何更改都不可见。实际上我不知道这是否是我应该覆盖的文件。

我已经检查了一些Stackoverflow问题,但我还没有找到答案。

FOSUser Bundle - Prevent Disabled users from logging in 这对我来说不起作用,因为用户被阻止了,我只需要覆盖异常消息,并使用另一个发送确认链接的链接创建重定向。

FOS user bundle authentication 我试图实现这里指出的解决方案,但它没有工作,我不确定我是否真的需要这么复杂的解决方案。

提前感谢您的帮助,有人需要查看我的文件,配置只是写,我会在这里发布必要的文件。

1 个答案:

答案 0 :(得分:2)

我终于想出了所有这些事情。如果有人遇到类似的问题,我建议您阅读Symfony文档中的这些主题: Overriding FOSUser B controllers Hooking into a controller

事实证明,对于所描述的功能,我需要覆盖FOSUser Bundle中的一些文件,有时Controller足够了,有时我需要修改EventListener(实际上我甚至创建了自己的事件)。有不止一种方式。

最难的部分是一键链接以重新设置密码。我使用了一个标志,在发送电子邮件时设置为false,并在单击链接时设置为true以防止再次使用链接。问题是,重置控制器被“执行”两次,所以当点击提交时有重定向,beacuse标志为真。我在会话中添加了一些计数,以便省略检查标志的代码部分,当你点击提交按钮时(第二次使用重置控制器中的重置方法),但是它只能阻止第二次点击提交,所以实际上你不能两次使用这个链接,但你可以看两次表格,这不是我想要达到的效果,但远比没有好。如果有人知道如何升级它,我将感激不尽

<?php



namespace My\UserBundle\Controller;

use FOS\UserBundle\Controller\ResettingController as FOSResettingController;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\Event\GetResponseUserEvent;
use FOS\UserBundle\Event\FilterUserResponseEvent;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use My\UserBundle\Entity\User;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use My\UserBundle\UserEvents;

/**
 * Controller managing the resetting of the password
 *
 * @author Thibault Duplessis <thibault.duplessis@gmail.com>
 * @author Christophe Coevoet <stof@notk.org>
 */
class ResettingController extends FOSResettingController
{


    /**
     * Request reset user password: submit form and send email
     */
    public function sendEmailAction(Request $request)
    {
        $username = $request->request->get('username');

        /** @var $user UserInterface */
        $user = $this->get('fos_user.user_manager')->findUserByUsernameOrEmail($username);

        if (null === $user) {
            return $this->render('FOSUserBundle:Resetting:request.html.twig', array(
                'invalid_username' => $username
            ));
        }

        if ($user->isPasswordRequestNonExpired($this->container->getParameter('fos_user.resetting.token_ttl'))) {
            return $this->render('FOSUserBundle:Resetting:passwordAlreadyRequested.html.twig');
        }

        if (null === $user->getConfirmationToken()) {
            /** @var $tokenGenerator \FOS\UserBundle\Util\TokenGeneratorInterface */
            $tokenGenerator = $this->get('fos_user.util.token_generator');
            $user->setConfirmationToken($tokenGenerator->generateToken());
        }

        $this->get('fos_user.mailer')->sendResettingEmailMessage($user);
        $user->setPasswordRequestedAt(new \DateTime());
        $user->setPasswordRequestedClicked(false);
        $this->get('fos_user.user_manager')->updateUser($user);

        $_SESSION['views'] = 1;

        return new RedirectResponse($this->generateUrl('fos_user_resetting_check_email',
            array('email' => $this->getObfuscatedEmail($user))
        ));
    }


    /**
     * Reset user password
     */
    public function resetAction(Request $request, $token)
    {
        /** @var $formFactory \FOS\UserBundle\Form\Factory\FactoryInterface */
        $formFactory = $this->get('fos_user.resetting.form.factory');
        /** @var $userManager \FOS\UserBundle\Model\UserManagerInterface */
        $userManager = $this->get('fos_user.user_manager');
        /** @var $dispatcher \Symfony\Component\EventDispatcher\EventDispatcherInterface */
        $dispatcher = $this->get('event_dispatcher');

        $user = $userManager->findUserByConfirmationToken($token);


        //Here there is a reaction for using expired token (column confirmation token === null) - redirection to page with possibility of sending another one.
        if (null === $user) {
               return $this->redirectToRoute('fos_user_invalid_token_click');

        }



        if ($_SESSION['views'] == 1){
            $event = new GetResponseUserEvent($user, $request);
            $dispatcher->dispatch(UserEvents::RESETTING_RESET_CLICK_CHECK, $event);

            if (null !== $event->getResponse()) {
                return $event->getResponse();
            } else {
                $user->setPasswordRequestedClicked(true);
                $userManager->updateUser($user);
                $_SESSION['views']++;
                $_SESSION['views']++;
            }
        } else {
            $_SESSION['views']++;
        }

        if ($_SESSION['views'] == 5){
            $event = new GetResponseUserEvent($user, $request);
            $dispatcher->dispatch(UserEvents::RESETTING_RESET_CLICK_CHECK, $event);

            if (null !== $event->getResponse()) {
                return $event->getResponse();
            }
        }



        $event = new GetResponseUserEvent($user, $request);
        $dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_INITIALIZE, $event);

        if (null !== $event->getResponse()) {
            return $event->getResponse();
        }



        $form = $formFactory->createForm();
        $form->setData($user);




        $form->handleRequest($request);


        if ($form->isValid()) {
            $event = new FormEvent($form, $request);
            $dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_SUCCESS, $event);


            $userManager->updateUser($user);

            if (null === $response = $event->getResponse()) {
                $url = $this->generateUrl('fos_user_profile_show');
                $response = new RedirectResponse($url);
            }

            $dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_COMPLETED, new FilterUserResponseEvent($user, $request, $response));

            return $response;
        }

        $userManager->updateUser($user);
        return $this->render('FOSUserBundle:Resetting:reset.html.twig', array(
            'token' => $token,
            'form' => $form->createView(),
        ));
    }



    public function InvalidTokenTtlMessageAction() {
        return $this->render('UserBundle:Resetting:invalidTokenTtlRes.html.twig');
    }

    public function InvalidTokenClickMessageAction() {
        return $this->render('UserBundle:Resetting:invalidTokenClickRes.html.twig');
    }


}

我的听众:

  <?php



namespace My\UserBundle\EventListener;

use FOS\UserBundle\EventListener\ResettingListener as FOSResettingListener;
use FOS\UserBundle\FOSUserEvents;
use My\UserBundle\UserEvents;
use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\Event\GetResponseUserEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use My\UserBundle\Entity\User;

class ResettingListener extends FOSResettingListener
{
    private $router;
    private $tokenTtl;

    public function __construct(UrlGeneratorInterface $router, $tokenTtl)
    {
        $this->router = $router;
        $this->tokenTtl = $tokenTtl;
    }

    public static function getSubscribedEvents()
    {
        return array(
            UserEvents::RESETTING_RESET_CLICK_CHECK => 'onResettingClickCheck',
            FOSUserEvents::RESETTING_RESET_INITIALIZE => 'onResettingResetInitialize',
            FOSUserEvents::RESETTING_RESET_SUCCESS => 'onResettingResetSuccess',

        );
    }

    public function onResettingClickCheck(GetResponseUserEvent $event){
        //checking if link hasn't expired due to its usage
        if ($event->getUser()->isPasswordRequestedClicked() === true){
            $event->setResponse(new RedirectResponse($this->router->generate('fos_user_invalid_token_click')));
        }
    }

    public function onResettingResetInitialize(GetResponseUserEvent $event)
    {
        //checking if link hasn't expired due to exceeding token Ttl
        if (!$event->getUser()->isPasswordRequestNonExpired($this->tokenTtl)) {
            $event->setResponse(new RedirectResponse($this->router->generate('fos_user_invalid_token_ttl')));
        }

    }

    public function onResettingResetSuccess(FormEvent $event)
    {

        /** @var $user \FOS\UserBundle\Model\UserInterface */
        $user = $event->getForm()->getData();

        $user->setConfirmationToken(null);
        $user->setPasswordRequestedAt(null);
        $user->setEnabled(true);



    }
}

和我的用户实体:

 <?php

namespace My\UserBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
use My\BackendBundle\Entity;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\Common\Collections\ArrayCollection;


/**
 * User
 *
 * @ORM\Table(name="user")
 * @ORM\Entity(repositoryClass="My\UserBundle\Repository\UserRepository")
 */
class User extends BaseUser
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer", nullable = false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;


    /**
     * @Gedmo\Slug(fields={"username"})
     * @ORM\Column(length=128, unique=true)
     */
    private $slug;


    /**
     *
     * @ORM\ManyToMany(targetEntity="\My\BackendBundle\Entity\Event", mappedBy="users")
     * @ORM\JoinColumn(name="id", referencedColumnName="id", nullable=false)
     * @ORM\OrderBy({"date"="ASC"})
     *
     */
    protected $events;



    /**
     * @var \Doctrine\Common\Collections\ArrayCollection $event_org
     * @ORM\OneToMany(targetEntity="\My\BackendBundle\Entity\Event", mappedBy="user_org", cascade={"all"})
     */
    protected $event_org;



    /**
     * @var \DateTime
     * @ORM\Column(name="confirmation_token_requested_at", type="datetime")
     */
    protected $confirmationTokenRequestedAt;


    /**
     * @var boolean
     * @ORM\Column(name="password_requested_clicked", type="boolean", nullable=true)
     */
    protected $passwordRequestedClicked;





    public function __toString()
    {
        return $this->getUsername();
    }


    /**
     * Get id
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    public function __construct()
    {
        parent::__construct();
        $this->event_org = new \Doctrine\Common\Collections\ArrayCollection();


    }

    /**
     * Add event
     *
     * @param \My\BackendBundle\Entity\Event $event
     *
     * @return User
     */
    public function addEvent(\My\BackendBundle\Entity\Event $event)
    {
        $this->events[] = $event;
        $event->addUser($this);

        return $this;
    }

    /**
     * Remove event
     *
     * @param \My\BackendBundle\Entity\Event $event
     */
    public function removeEvent(\My\BackendBundle\Entity\Event $event)
    {
        $this->events->removeElement($event);
    }

    /**
     * Get events
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getEvents()
    {
        return $this->events;
    }






    /**
     * Set slug
     *
     * @param string $slug
     *
     * @return User
     */
    public function setSlug($slug)
    {
        $this->slug = $slug;

        return $this;
    }

    /**
     * Get slug
     *
     * @return string
     */
    public function getSlug()
    {
        return $this->slug;
    }

    /**
     * Add eventOrg
     *
     * @param \My\BackendBundle\Entity\Event $eventOrg
     *
     * @return User
     */
    public function addEventOrg(\My\BackendBundle\Entity\Event $eventOrg)
    {
        $this->event_org[] = $eventOrg;

        return $this;
    }

    /**
     * Remove eventOrg
     *
     * @param \My\BackendBundle\Entity\Event $eventOrg
     */
    public function removeEventOrg(\My\BackendBundle\Entity\Event $eventOrg)
    {
        $this->event_org->removeElement($eventOrg);
    }

    /**
     * Get eventOrg
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getEventOrg()
    {
        return $this->event_org;
    }

        /**
     * Set confirmationTokenRequestedAt
     *
     * @param \DateTime $confirmationTokenRequestedAt
     *
     * @return User
     */
    public function setConfirmationTokenRequestedAt(\DateTime $date = null)
    {
        $this->confirmationTokenRequestedAt = $date;

        return $this;
    }

    /**
     * Gets the timestamp that the user requested a confirmation_token.
     *
     * @return null|\DateTime
     */
    public function getConfirmationTokenRequestedAt()
    {
        return $this->confirmationTokenRequestedAt;
    }

    public function isConfirmationTokenNonExpired($ttl)
    {

        return $this->getConfirmationTokenRequestedAt() instanceof \DateTime &&
        $this->getConfirmationTokenRequestedAt()->getTimestamp() + $ttl > time();
    }

    /**
     * Set passwordRequestedClicked
     *
     * @param boolean $passwordRequestedClicked
     *
     * @return User
     */
    public function setPasswordRequestedClicked($boolean)
    {
        $this->passwordRequestedClicked = (Boolean) $boolean;

        return $this;
    }

    /**
     * Get passwordRequestedClicked
     *
     * @return boolean
     */
    public function getPasswordRequestedClicked()
    {
        return $this->passwordRequestedClicked;
    }

    /**
     * Checks whether the user has used password request.
     *
     *
     * @return Boolean true if the user is enabled, false otherwise
     */
    public function isPasswordRequestedClicked() {
        return $this->passwordRequestedClicked;
    }
}

如果有人希望获取剩余问题的代码,请写信给我,我会在这里提供:)。

重置控制器:

<?php
namespace My\UserBundle\Controller;

use FOS\UserBundle\Controller\ResettingController as FOSResettingController;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\Event\GetResponseUserEvent;
use FOS\UserBundle\Event\FilterUserResponseEvent;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use My\UserBundle\Entity\User;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use My\UserBundle\UserEvents;

class ResettingController extends FOSResettingController

{

public function sendEmailAction(Request $request)
{
    $username = $request->request->get('username');

    $user = $this->get('fos_user.user_manager')->findUserByUsernameOrEmail($username);

    if (null === $user) {
        return $this->render('FOSUserBundle:Resetting:request.html.twig', array(
            'invalid_username' => $username
        ));
    }

    if ($user->isPasswordRequestNonExpired($this->container->getParameter('fos_user.resetting.token_ttl'))) {
        return $this->render('FOSUserBundle:Resetting:passwordAlreadyRequested.html.twig');
    }

    if (null === $user->getConfirmationToken()) {
        $tokenGenerator = $this->get('fos_user.util.token_generator');
        $user->setConfirmationToken($tokenGenerator->generateToken());
    }

    $this->get('fos_user.mailer')->sendResettingEmailMessage($user);
    $user->setPasswordRequestedAt(new \DateTime());
    $user->setPasswordRequestedClicked(false);
    $this->get('fos_user.user_manager')->updateUser($user);

    $_SESSION['views'] = 1;

    return new RedirectResponse($this->generateUrl('fos_user_resetting_check_email',
        array('email' => $this->getObfuscatedEmail($user))
    ));
}


public function resetAction(Request $request, $token)
{
    $formFactory = $this->get('fos_user.resetting.form.factory');
    $userManager = $this->get('fos_user.user_manager');
    $dispatcher = $this->get('event_dispatcher');

    $user = $userManager->findUserByConfirmationToken($token);


    //Here there is a reaction for using expired token (column confirmation token === null) - redirection to page with possibility of sending another one.
    if (null === $user) {
           return $this->redirectToRoute('fos_user_invalid_token_click');

    }

    if ($_SESSION['views'] == 1){
        $event = new GetResponseUserEvent($user, $request);
        $dispatcher->dispatch(UserEvents::RESETTING_RESET_CLICK_CHECK, $event);

        if (null !== $event->getResponse()) {
            return $event->getResponse();
        } else {
            $user->setPasswordRequestedClicked(true);
            $userManager->updateUser($user);
            $_SESSION['views']++;
            $_SESSION['views']++;
        }
    } else {
        $_SESSION['views']++;
    }

    if ($_SESSION['views'] == 5){
        $event = new GetResponseUserEvent($user, $request);
        $dispatcher->dispatch(UserEvents::RESETTING_RESET_CLICK_CHECK, $event);

        if (null !== $event->getResponse()) {
            return $event->getResponse();
        }
    }

    $event = new GetResponseUserEvent($user, $request);
    $dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_INITIALIZE, $event);

    if (null !== $event->getResponse()) {
        return $event->getResponse();
    }


    $form = $formFactory->createForm();
    $form->setData($user);

    $form->handleRequest($request);


    if ($form->isValid()) {
        $event = new FormEvent($form, $request);
        $dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_SUCCESS, $event);


        $userManager->updateUser($user);

        if (null === $response = $event->getResponse()) {
            $url = $this->generateUrl('fos_user_profile_show');
            $response = new RedirectResponse($url);
        }

        $dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_COMPLETED, new FilterUserResponseEvent($user, $request, $response));

        return $response;
    }

    $userManager->updateUser($user);
    return $this->render('FOSUserBundle:Resetting:reset.html.twig', array(
        'token' => $token,
        'form' => $form->createView(),
    ));
}



public function InvalidTokenTtlMessageAction() {
    return $this->render('UserBundle:Resetting:invalidTokenTtlRes.html.twig');
}

public function InvalidTokenClickMessageAction() {
    return $this->render('UserBundle:Resetting:invalidTokenClickRes.html.twig');
}
}