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
只是 概念 ,因此您无法访问其他 Entities
或 EntityManager
来自 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)
{
...
}
}
我正在使用 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
只是 概念 ,因此您无法访问其他 Entities
或 EntityManager
来自 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)
{
...
}
}