FOS 用户包使用来自 ORM 数据库的参数

FOS User Bundle using parameters from ORM Database

我正在使用 FOS User Bundle,我需要使用另一个 Entity/table 的记录中的一个名为 "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']);

显然,现在我无权访问存储库。因为我不在控制器中,所以我不确定应该如何从实体的函数内部使用这些值。

您可能误解了Entity的概念:

The class - often called an "entity", meaning a basic class that holds data - is simple and helps fulfill the business requirement of needing products in your application. This class can't be persisted to a database yet - it's just a simple PHP class

这意味着 Entity 只是 概念 ,因此您无法访问其他 EntitiesEntityManager 来自 class.

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

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

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

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

恕我直言,我能想到一个更合适的方法来解决这个问题:

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: 代码未测试

最后的解决方案是使用具有相同功能的另一个函数覆盖 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)
    {
         ...
    }
}