一旦从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 >= 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 >= 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());
}
}
感谢您的建议和意见!
答案 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")