在Symfony2中检查具有多个属性的User上的登录

Acy*_*yra 3 symfony

我正在Symfony2中构建一个多租户应用程序.对于安全的"管理"区域,我有一个自定义实体提供商(请参阅:http://symfony.com/doc/current/cookbook/security/entity_provider.html)

但是,似乎Symfony2仅支持在单个属性上检查实体.

my_entity_provider:
            entity:
                class:              SecurityBundle:User
                property:           email
Run Code Online (Sandbox Code Playgroud)

但是,在我的应用中,单个用户可以拥有多个具有相同电子邮件地址的帐户.我需要的是在登录时检查租户ID属性.

my_entity_provider:
            entity:
                class:              SecurityBundle:User
                property:           email, tenantID
Run Code Online (Sandbox Code Playgroud)

我不知道如何在Symfony2中实现这一目标.我在创建新用户时已经能够覆盖loadUsername方法,但是Symfony2安全性中的login_check并没有使用它(这真的很难看).

 public function loadUserByUsername($username)
    {
        /* we create a concatenated string in the  User entity to pass both
        the email and tenantId values as the "username" */
        $user_parts = explode("|", $username); 

        $q = $this->createQueryBuilder('u')
            ->where('u.tenantId = :tenantid AND u.email = :email')
            ->setParameter('tenantID', $user_parts[1])
            ->setParameter('email', $user_parts[0])
            ->getQuery();

        try { 
            $user = $q->getSingleResult();

        } catch (NoResultException $e) {
            throw new UsernameNotFoundException(sprintf('Unable to find an active User object identified by "%s".', $username), null, 0, $e);
        }

        return $user;
    }
Run Code Online (Sandbox Code Playgroud)

有关实现具有多个属性的自定义安全提供程序的指导?谢谢!

Flo*_*ian 6

你说你使用自定义身份验证提供程序,但我没有看到.我认为您使用默认实体提供程序,它实际上使用关联实体Repositiory.

如果您property在配置中定义了它,它将使用存储库``findOneBy`方法.

否则,如果您定义了自定义repositoryClass,并且此自定义资源库实现UserPrviderInterface,则symfony 将调用loadUserByUsername

因此,对于您调用loadUserByUsername方法的示例,只需property: email从security.yml中删除即可

否则,我会想到许多更清洁的解决方案,但我最关心的是

这是一个简单的过程:

  • 创建一个实现UserProviderInterface的类
  • 创建该类的服务(FE与ID: app.security.user.provider)
  • 配置security.yml以使用此服务

    1. 类:(伪代码,别忘了添加用语句,...)

class UserProvider implements UserProviderInterface
{
public function __construct(ManagerRegistry $doctrine, $tenantIdProvider)
{
     $this->doctrine = $doctrine;
     $this->tenantIdProvider = $tenantIdProvider;
}

public function loadUserByUsername($username)
{
    $this->doctrine->getRepository('App\Entity\User')->findOneBy([
        'email' => $username,
        'tenant' => $this->tenantIdProvider->getId(), // HERE $tenantIdProvider can be your listener for example.
    ]);
}

// more methods maybe?
}
Run Code Online (Sandbox Code Playgroud)
  1. 服务(services.yml,app/config/config.yml,...你想要的地方)

服务:

app.security.user.provider:
    class: UserProvider
    arguments:
        - '@doctrine'
        - '@app.tenant_id.provider' #maybe?
Run Code Online (Sandbox Code Playgroud)
  1. 安全配置

安全:

providers:
    user:
        id: app.security.user.provider
Run Code Online (Sandbox Code Playgroud)