使用ORM数据库中的参数的FOS用户包

时间:2017-11-28 09:37:40

标签: symfony fosuserbundle

我正在使用FOS用户包,我需要从我用于参数的另一个实体/表的记录中使用一个名为“maxLoginAttempts”的值。

实体它叫做参数。这是我当前的代码,我想从数据库中更改数字5的数字。

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;

/**
 * @ORM\Entity
 * @ORM\Table(name="`user`")
 */
class User extends BaseUser
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(type="integer")
     */
    protected $id;

    ...

    public function isAccountNonLocked()
    {
        if($this->getLoginAttempts() >= 5) {
            return false;
        } else {
            return true;
        }
     }
 }

我想象的是:

  $this->em->getRepository('AppBundle:Parameters')
        ->findOneBy(['name' => 'maxLoginAttempts']);

显然现在我无法访问存储库。因为我不在Controller中,所以我不确定如何在实体的函数内使用这些值。

3 个答案:

答案 0 :(得分:0)

您可能误解了Entity

的概念
  

该类 - 通常称为&#34;实体&#34;,意味着保存数据的基本类 - 很简单,有助于满足在您的应用程序中需要产品的业务需求。这个类还不能持久化到数据库 - 它只是一个简单的PHP类

这意味着Entity只是概念,因此您无法访问其他EntitiesEntityManager来自班级内部。

如果你想使用你所描述的成员函数之类的东西。你应该将maxLoginAttempts作为arg传递:

public function isAccountNonLocked($maxLoginAttempts)
{
    if($this->getLoginAttempts() >= maxLoginAttempts) {
        return false;
    } else {
        return true;
    }
}

在这种情况下,您需要先从配置maxLoginAttempts获取Entity的值,然后在您想要的User对象上使用它检查:

$maxLoginAttempts = $this->em->getRepository('AppBundle:Parameters')
        ->findOneBy(['name' => 'maxLoginAttempts']);
$user = $this->em->getRepository('AppBundle:User')->find($userId);
if ($user->isAccountNonLocked($maxLoginAttempts)) {
   // do something
}

答案 1 :(得分:0)

我可以想出一个更合适的方式,恕我直言,来解决这个问题:

User实体将有一个额外的属性 $ loginAttempts ,每次登录失败时,它将通过incrementLoginAttempts()方法递增。它将通过ORM初始化为0,方法isLocked()将告诉我们是否已达到5次尝试。

<?php
// AppBundle/Entity/User.php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;

/**
 * @ORM\Entity
 * @ORM\Table(name="`user`")
 */
class User extends BaseUser
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(type="integer")
     */
    protected $id;
    public function __construct()
    {
        parent::__construct();
    }

    /** 
     * @ORM\Column(type="integer",options={"default"=0}) 
     */
    private $loginAttempts;

    ...
    public function getLoginAttempts()
    {
       return $this->loginAttemps;
    }
    public function incrementLoginAttempts()
    {
       if($this->loginAttempts<5){
             $this->loginAttempts++;
       }
       return $this;
    }
    public function isLocked()
    {
        return ($this->loginAttempts == 5)
    }
    public function resetLoginAttempts()
    {
        $this->loginAttempts =0;
        return $this;
    }

然后,为SecuritySubscriber事件创建一个EventSubscriber,并在每次登录失败时触发incrementLoginAttempts();同时检查用户是否已被锁定

    <?php
// src/AppBundle/EventSubscriber/SecuritySubscriber.php
namespace AppBundle\EventSubscriber;

use AppBundle\Entity\User;  

class SecuritySubscriber implements EventSubscriberInterface  
{

    private $entityManager;
    private $tokenStorage;
    private $authenticationUtils;

    public function __construct(EntityManager $entityManager, TokenStorageInterface $tokenStorage, AuthenticationUtils $authenticationUtils)
    {
        $this->entityManager = $entityManager;
        $this->tokenStorage = $tokenStorage;
        $this->authenticationUtils = $authenticationUtils;
    }

    public static function getSubscribedEvents()
    {
        return array(
            AuthenticationEvents::AUTHENTICATION_FAILURE => 'onAuthenticationFailure',
        );
    }

    public function onAuthenticationFailure( AuthenticationFailureEvent $event )
    {
        $existingUser = $this->entityManager->getRepository(User::class)->findOneBy(['username' => $username]);

        if ($existingUser) {
            $existingUser->incrementLoginAttempts();
            $this->entityManager->persist($existingUser);
            $this->entityManager->flush();
            if($existingUser->isLocked()){
                // Do your logic here
                // Do not forget to un  $existingUser->resetLoginAttempts() when necessary
            }
        }
    }
}

不要忘记将订户注册为服务

# app/config/services.yml
services:  
    app.security.authentication_event_listener:
        class: AppBundle\EventSubscriber\SecuritySubscriber
        arguments:
            - "@doctrine.orm.entity_manager"
            - "@security.token_storage"
            - "@security.authentication_utils"

P.S:代码尚未经过测试。

答案 2 :(得分:0)

最后,解决方案是使用具有相同功能的其他功能覆盖UserChecker。

<?php

namespace AppBundle\Checker;

use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Exception\CredentialsExpiredException;
use Symfony\Component\Security\Core\Exception\DisabledException;
use Symfony\Component\Security\Core\Exception\LockedException;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Symfony\Component\Security\Core\User\UserChecker as BaseUserChecker;
use Symfony\Component\Security\Core\User\UserInterface;

class UserChecker extends BaseUserChecker
{
    private $em;

    public function __construct( EntityManagerInterface $em)
    {
        $this->em = $em;
    }

    public function checkPreAuth(UserInterface $user)
    {
        //parent::checkPreAuth($user);
        $maxMinutesLocked = $this->em->getRepository('AppBundle:Parameters')->findOneBy(array('name' => 'maxTimeLocked'))->getValue();

        if (!$user instanceof AdvancedUserInterface) {
            return;
        }

        //So I just added a new function called isAccountLocked() to the User Entity that's a copy from isAccountNonLocked() but I could add a paramater
        if ($user->isAccountLocked($maxMinutesLocked)) {
            $ex = new LockedException('User account is locked.');
            $ex->setUser($user);
            throw $ex;
        }

        if (!$user->isEnabled()) {
            ...
        }
        if (!$user->isAccountNonExpired()) {
            ...
        }
    }

    public function checkPostAuth(UserInterface $user)
    {
         ...
    }
}