Symfony2 用户名或电子邮件登录

5
我正在使用Symfony2的标准身份验证机制,并希望用户可以使用用户名或电子邮件登录,但我无法找出为什么它不起作用。我已经测试了存储库类,并且它按预期工作。我遵循了这个how-to
这是我的用户提供程序类:
<?php

namespace My\UserBundle\Entity;

use Doctrine\ORM\EntityRepository ,
Symfony\Component\Security\Core\User\UserProviderInterface ,
Symfony\Component\Security\Core\User\UserInterface;

/**
* UserRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class UserRepository extends EntityRepository implements UserProviderInterface 
{
    function loadUserByUsername($username)
    {

        $qb = $this->createQueryBuilder('u') ;
        return 
        $qb->select('u')
        ->where(
            $qb->expr()->orx(
                $qb->expr()->like('u.username' ,':username') ,
                $qb->expr()->like('u.email' ,':username')
            )
        )
        //->andWhere($qb->expr()->eq('u.enabled' ,'true') )
        ->setParameters(array('username' =>$username ) )        
        ->getQuery()
        ->getResult() ;                  
    }

    function refreshUser(UserInterface $user)
    {
        return $this->loadUserByUsername($user->getUsername() );
    }

    function supportsClass($class)
    {
        return $class === 'My\UserBundle\Entity\User';
    }

}

你说的“不工作”是什么意思?请更详细地描述一下。 - Alessandro Desantis
请将您对问题的解决方案移动到答案中并接受它。 - Elnur Abdurrakhimov
大家好,我明天之前不能接受自己的答案:P - kosaidpo
FFR,我发现这是一个很好的指南:https://github.com/FriendsOfSymfony/FOSUserBundle/blob/1.2.0/Resources/doc/logging_by_username_or_email.md#extending-the-usermanager-class - Adam Monsen
4个回答

9
我提出了一个更简单的方法,只需要编辑security.yml文件。
您需要创建两个不同的安全提供程序,但两者都使用相同的用户类。第一个具有用户名作为属性,第二个具有电子邮件作为属性。
然后,您创建一个chain_provider,其中包括这两个提供程序,并在防火墙部分中使用它。
security:

    providers:
        chain_provider:
            chain:
                providers: [db_username, db_email]
        db_username:
            entity:
                class: MyUserBundle:User
                property: username
        db_email:
            entity:
                class: MyUserBundle:User
                property: email

    firewalls:

        default:
            anonymous: ~            
            provider: chain_provider
            form_login:
                login_path: /login
                check_path: /login

我不知道这种方法是否是一种干净的做法,但它快速、简单且效果良好。


我还没有测试过这个,但看起来是一个非常优雅的解决方案。 - someuser

2

大家好,问题出在我的security.yml文件中,我有以下配置:

providers:
        main:
            entity: { class: My\UserBundle\Entity\User ,property : username}

所以我需要去掉property :username这个参数。


2
根据Anand和David Barrat的回答,这似乎是一个粗糙的、最多只能算是临时解决方案,而不是一个正确的解决方案。 - Marcus

1

去掉属性:username,让UserProviderInterface在登录时按预期加载用户,但不会像预期的那样调用refreshUser()方法。我进行了检查以查看它是否被调用,但没有。

每次访问重新加载用户的类是ContextListener::refreshUser(TokenInterface $ token)方法。在此界面中,接口对UserProviders进行迭代,并调用首先返回非空用户的refreshUser。

我可以确定这一点,因为在原始加载中,我将所有不同的实体组合在一起,以便只进行一次SQL调用,而当用户重新加载时,它会调用7次。

此外,EntityUserProvider::refreshUser()方法不调用存储库的方法,而是直接从数据库重新加载。


EntityUserProvider 类中添加以下代码可解决 refreshUser 问题:if($this->repository instanceof UserProviderInterface) { return $this->repository->refreshUser($user); } - Anand
1
编辑Symfony似乎是一个大忌,因为它会破坏更新Symfony的能力。 - David Barratt

0

您的提供程序类是正确的,而且您也是正确的,问题确实出在security.yml文件中,但是您的解决方案是不正确的。

根据文档,您的security.yml文件应该如下所示:

security:
# ...
providers:
    administrators:
        entity: { class: MyUserBundle:User }

请注意,类被定义为Bundle,而不是直接的类。 目前您的代码方式,Symfony完全忽略了您的存储库类,直到您正确定义了security.yml。正如@Anand指出的那样,仅删除属性并不会调用refreshUser。但是,如果您正在使用自己的存储库,则不需要定义该属性(因为它已在查询中定义)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接