Symfony UserProviderInterface和安全提供程序实体

时间:2015-03-05 13:43:30

标签: symfony

一旦从security.yml中删除property:username,我就无法通过表单登录。

只看到错误:

Authentication request could not be processed due to a system problem.

我可以在哪里开始调试?

app/logs/dev.log我得到

security.INFO: Authentication request failed: The Doctrine repository
"Doctrine\ORM\EntityRepository" must implement UserProviderInterface.

这是什么意思?

这是我在http://symfony.com/doc/current/cookbook/security/entity_provider.html

之后到目前为止所得到的

security.yml

security:
    providers:
        administrators:
            entity: { class:AcmePasswordBundle:User, property: username }

    encoders:
        Acme\PasswordBundle\Entity\User:
            algorithm: bcrypt
            #cost: 12

    role_hierarchy:
        ROLE_ADMIN: ROLE_USER
        ROLE_SUPER_ADMIN: [ ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH ]

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt|error)|css|images|js)/
            security: false

        admin_area:
            anonymous: ~
            pattern: .*
            form_login:
                login_path: acme_password_login
                check_path: acme_password_login_check
                target_path_parameter: _target_path
            logout:
                path:   /logout
                target: /

    access_control:
        - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/free, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/, roles: ROLE_ADMIN }

实体\ user.php的

<?php

namespace Acme\PasswordBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;

class User implements AdvancedUserInterface, \Serializable
{
    /**
     * @var integer
     */
    private $id;

    /**
     * @var string
     */
    private $username;

    /**
     * @var string
     */
    private $email;

    /**
     * @var string
     */
    private $password;

    /**
     * @var boolean
     */
    private $isActive;

    public function __construct()
    {
        $this->isActive = true;
    }

    /**
     * @return integer
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @return string
     */
    public function getUsername()
    {
        return $this->username;
    }

    /**
     * @return string
     */
    public function getEmail()
    {
        return $this->email;
    }

    /**
     * @return string
     */
    public function getPassword()
    {
        return $this->password;
    }

    /**
     * @param string $username
     * @return User
     */
    public function setUsername($username)
    {
        $this->username = $username;

        return $this;
    }

    /**
     * @param string $email
     * @return User
     */
    public function setEmail($email)
    {
        $this->email = $email;

        return $this;
    }

    /**
     * @param string $password
     * @return User
     */
    public function setPassword($password)
    {
        $this->password = $password;

        return $this;
    }


    /**
     * (PHP 5 &gt;= 5.1.0)<br/>
     * String representation of object
     * @link http://php.net/manual/en/serializable.serialize.php
     * @return string the string representation of the object or null
     */
    public function serialize()
    {
        return serialize(array(
            $this->id,
            $this->username,
            $this->password,
            // see section on salt below
            // $this->salt,
        ));
    }

    /**
     * (PHP 5 &gt;= 5.1.0)<br/>
     * Constructs the object
     * @link http://php.net/manual/en/serializable.unserialize.php
     * @param string $serialized <p>
     * The string representation of the object.
     * </p>
     * @return void
     */
    public function unserialize($serialized)
    {
        list (
            $this->id,
            $this->username,
            $this->password,
            // see section on salt below
            // $this->salt
        ) = unserialize($serialized);
    }

    /**
     * Returns the roles granted to the user.
     *
     * <code>
     * public function getRoles()
     * {
     *     return array('ROLE_USER');
     * }
     * </code>
     *
     * Alternatively, the roles might be stored on a ``roles`` property,
     * and populated in any number of different ways when the user object
     * is created.
     *
     * @return Role[] The user roles
     */
    public function getRoles()
    {
        return array('ROLE_ADMIN');
    }

    /**
     * Returns the salt that was originally used to encode the password.
     *
     * This can return null if the password was not encoded using a salt.
     *
     * @return string|null The salt
     */
    public function getSalt()
    {
        return null;
    }

    /**
     * Removes sensitive data from the user.
     *
     * This is important if, at any given point, sensitive information like
     * the plain-text password is stored on this object.
     */
    public function eraseCredentials()
    {
        // TODO: Implement eraseCredentials() method.
    }

    /**
     * Checks whether the user's account has expired.
     *
     * Internally, if this method returns false, the authentication system
     * will throw an AccountExpiredException and prevent login.
     *
     * @return bool true if the user's account is non expired, false otherwise
     *
     * @see AccountExpiredException
     */
    public function isAccountNonExpired()
    {
        return true;
    }

    /**
     * Checks whether the user is locked.
     *
     * Internally, if this method returns false, the authentication system
     * will throw a LockedException and prevent login.
     *
     * @return bool true if the user is not locked, false otherwise
     *
     * @see LockedException
     */
    public function isAccountNonLocked()
    {
        return true;
    }

    /**
     * Checks whether the user's credentials (password) has expired.
     *
     * Internally, if this method returns false, the authentication system
     * will throw a CredentialsExpiredException and prevent login.
     *
     * @return bool true if the user's credentials are non expired, false otherwise
     *
     * @see CredentialsExpiredException
     */
    public function isCredentialsNonExpired()
    {
        return true;
    }

    /**
     * Checks whether the user is enabled.
     *
     * Internally, if this method returns false, the authentication system
     * will throw a DisabledException and prevent login.
     *
     * @return bool true if the user is enabled, false otherwise
     *
     * @see DisabledException
     */
    public function isEnabled()
    {
        return $this->isActive;
    }
}

实体\ UserRepository.php

<?php

namespace Acme\PasswordBundle\Entity;

use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\NoResultException;

class UserRepository extends EntityRepository implements UserProviderInterface
{

    /**
     * Loads the user for the given username.
     *
     * This method must throw UsernameNotFoundException if the user is not
     * found.
     *
     * @param string $username The username
     *
     * @return UserInterface
     *
     * @see UsernameNotFoundException
     *
     * @throws UsernameNotFoundException if the user is not found
     */
    public function loadUserByUsername($username)
    {
        $q = $this
            ->createQueryBuilder('u')
            ->where('u.username = :username OR u.email = :email')
            ->setParameter('username', $username)
            ->setParameter('email', $username)
            ->getQuery();

        try {
            // The query::getSingleResult() method throws an exception
            // if there is no record matching the criteria.
            $user = $q->getSingleResult();
        } catch (NoResultException $e) {
            $message = sprintf(
                'Unable to find an active admin AcmePasswordBundle:User object identified by "%s".',
                $username
            );
            throw new UsernameNotFoundException($message, 0, $e);
        }
        return $user;
    }

    /**
     * Refreshes the user for the account interface.
     *
     * It is up to the implementation to decide if the user data should be
     * totally reloaded (e.g. from the database), or if the UserInterface
     * object can just be merged into some internal array of users / identity
     * map.
     *
     * @param UserInterface $user
     *
     * @return UserInterface
     *
     * @throws UnsupportedUserException if the account is not supported
     */
    public function refreshUser(UserInterface $user)
    {
        $class = get_class($user);
        if(!$this->supportsClass($class)) {
            throw new UnsupportedUserException(
                sprintf(
                    'Instances of "%s" are not supported.',
                    $class
                )
            );
        }
        return $this->find($user->getId());
    }

    /**
     * Whether this provider supports the given user class.
     *
     * @param string $class
     *
     * @return bool
     */
    public function supportsClass($class)
    {
        return $this->getEntityName() === $class
            || is_subclass_of($class, $this->getEntityName());
    }

}

感谢您的建议和意见!

2 个答案:

答案 0 :(得分:2)

更新:如果存储库还实现了UserProviderInterface,我不知道实体用户提供程序将直接使用存储库。

以下是使用UserProvider服务的替代方法。

详情请见:http://symfony.com/doc/current/cookbook/security/custom_provider.html

建立独立的用户提供商

class UserProvider implements UserProviderInterface
{
    private $userRepository;

    public function __construct($userRepository)
    {
        $this->userRepository = $userRepository;
    }
public function loadUserByUsername($username)
{
    $q = $this->userRepository->  // This is the only difference
        ->createQueryBuilder('u')
        ->where('u.username = :username OR u.email = :email')
        ->setParameter('username', $username)
        ->setParameter('email', $username)
        ->getQuery();

    try {
        // The query::getSingleResult() method throws an exception
        // if there is no record matching the criteria.
        $user = $q->getSingleResult();
    } catch (NoResultException $e) {
        $message = sprintf(
            'Unable to find an active admin AcmePasswordBundle:User object identified by "%s".',
            $username
        );
        throw new UsernameNotFoundException($message, 0, $e);
    }
    return $user;
}
// Rest of user provider functions

连接:

# security.yml
providers:
    acme_password_user_provider:
        id: acme_password.user_provider

        form_login:
            provider:            acme_password_user_provider
            login_path:          acme_password_login

# services.yml


acme_password.user_repository:
    class:  Acme\PasswordBundle\Entity\UserRepository
    factory_service: 'doctrine.orm.default_entity_manager'
    factory_method:  'getRepository'
    arguments:  
        - 'Acme\PasswordBundle\Entity\User'

acme_password.user_provider:
    class:  Acme\PasswordBundle\Security\UserProvider
    arguments: ['@acme_password.user_repository']

当然,正如@AlpineCoder所说,您需要确保您的存储库已连接到您的实体:

# User.orm.yml
Acme\PasswordBundle\Entity\User:
    type:  entity
    table: users
    repositoryClass: Acme\PasswordBundle\Entity\UserRepository

但重点是将用户提供商定义为服务。

答案 1 :(得分:1)

您没有显示您的实体的配置方式,但您需要告诉Doctrine将您的自定义存储库用于User实体。在注释中,这看起来像:

 @ORM\Entity(repositoryClass="Vendor\Project\Entity\UserRepository")