用户不是';t登录Symfony 2.3


User isn't Authenticated after Login Symfony 2.3

我正在研究Symfony 2,并试图创建我的身份验证用户。我遵循文档,但如果我实现AdvancedUserInterface,我的用户没有经过身份验证,相反,如果我使用UserInterface,一切都很好。

我的代码是:

控制器:

public function saveAction(Request $request) {
        $user = new User();
        $form = $this->createForm(new UserType(), $user);
        $form->handleRequest($request);
        if ($form->isValid()) {
            $em = $this->getDoctrine()->getManager();
            $encodeFactory = $this->container->get('security.encoder_factory');
            $encoder = $encodeFactory->getEncoder($user);
            $user->setPassword($encoder->encodePassword($request->get($form->getName())['password'], $user->getSalt()));
            $em->persist($user);
            $em->flush();
            return $this->redirect($this->generateUrl('index'));
        }
        return array(
            'form' => $form
        );

实体:

<?php
namespace FOO'UserBundle'Entity;
use Doctrine'ORM'Mapping as ORM;
use Symfony'Component'Security'Core'Role'Role;
use Symfony'Component'Security'Core'User'AdvancedUserInterface;
/**
 * User
 *
 * @ORM'Table()
 * @ORM'Entity(repositoryClass="FOO'UserBundle'Entity'UserRepository")
 */
class User implements AdvancedUserInterface, 'Serializable {
    /**
     * @var integer
     *
     * @ORM'Column(name="id", type="integer")
     * @ORM'Id
     * @ORM'GeneratedValue(strategy="AUTO")
     */
    private $id;
    /**
     * @var string
     *
     * @ORM'Column(name="username", type="string", length=255)
     */
    private $username;
    /**
     * @var string
     *
     * @ORM'Column(name="salt", type="string", length=255)
     */
    private $salt;
    /**
     * @var string
     *
     * @ORM'Column(name="password", type="string", length=255)
     */
    private $password;
    /**
     * @var boolean
     *
     * @ORM'Column(name="is_active", type="boolean")
     */
    private $isActive;

    public function __construct(){
        $this->isActive = true;
        $this->salt= md5(uniqid(null, true));
    }
    /**
     * Get id
     *
     * @return integer
     */
    public function getId() {
        return $this->id;
    }
    /**
     * Set username
     *
     * @param string $username
     * @return User
     */
    public function setUsername($username) {
        $this->username = $username;
        return $this;
    }
    /**
     * Get username
     *
     * @return string
     */
    public function getUsername() {
        return $this->username;
    }
    /**
     * Set salt
     *
     * @param string $salt
     * @return User
     */
    public function setSalt($salt) {
        $this->salt = $salt;
        return $this;
    }
    /**
     * Get salt
     *
     * @return string
     */
    public function getSalt() {
        return $this->salt;
    }
    /**
     * Set password
     *
     * @param string $password
     * @return User
     */
    public function setPassword($password) {
        $this->password = $password;
        return $this;
    }
    /**
     * Get password
     *
     * @return string
     */
    public function getPassword() {
        return $this->password;
    }
    /**
     * Set isActive
     *
     * @param boolean $isActive
     * @return User
     */
    public function setIsActive($isActive)
    {
        $this->isActive = $isActive;
        return $this;
    }
    /**
     * Get isActive
     *
     * @return boolean 
     */
    public function getIsActive()
    {
        return $this->isActive;
    }
    /**
     * 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;
    }
    /**
     * (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,
            $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,
            $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_USER');
    }
    /**
     * 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() {
    }
}

security.yml

security:
    encoders:
        Symfony'Component'Security'Core'User'User: plaintext
        FOO'UserBundle'Entity'User:
            algorithm: sha512
    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
    providers:
        administrators:
            entity:
                class: FOOUserBundle:User
                property: username
        in_memory:
            memory:
                users:
                    user:  { password: userpass, roles: [ 'ROLE_USER' ] }
                    admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] }
    firewalls:
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false
        FOO:
            pattern:   ^/             
            anonymous: ~              
            form_login:
                login_path: login                 
                check_path: login_check           
                csrf_provider: form.csrf_provider 
            logout:
                path:   /logout                  
                target: /                        
            remember_me:
                key:      "%secret%"
                lifetime: 31536000 # 365 giorni in secondi
                path:     /
                domain:   ~ # Defaults to the current domain from $_SERVER
            #anonymous: ~
            #http_basic:
            #    realm: "Secured Demo Area"
    access_control:
        - { path: ^/demo/secured/hello/admin/, roles: ROLE_ADMIN }
        #- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }

在取消序列化时,不会调用类的构造函数。您可以通过创建一个新的Object并像这样序列化和取消序列化来轻松地检查这一点:

$user = new 'FOO'UserBundle'EntityUser();
var_dump($user);
$serialized = serialize($user);
var_dump($serialized);
$uUser = unserialize($serialized);
var_dump($uUser);
/**
It output something like this:
class FOO'UserBundle'Entity'User#1 (5) {
  private $id =>
  NULL
  private $username =>
  NULL
  private $salt =>
  string(32) "296cb7bebc4aff07a3cc5bb0da746707"
  private $password =>
  NULL
  private $isActive =>
  bool(true)
}
string(107) "C:26:"FOO'UserBundle'Entity'User":68:{a:4:{i:0;N;i:1;N;i:2;N;i:3;s:32:"296cb7bebc4aff07a3cc5bb0da746707";}}"
class FOO'UserBundle'Entity'User#2 (5) {
  private $id =>
  NULL
  private $username =>
  NULL
  private $salt =>
  string(32) "296cb7bebc4aff07a3cc5bb0da746707"
  private $password =>
  NULL
  private $isActive =>
  NULL
}
*/

因此,您必须将isActive添加到serialize/unserialize方法中,或者在unserialize方法中手动调用构造函数。

这种行为背后的真正问题,可以在AbstractToken类中找到:Symfony在配置的用户提供程序发出的每个请求时都会重新加载用户,并将其设置为Token实例(从AbstractToken继承setUser方法)。在这个方法Symfony中,如果用户更改了,请对照未序列化的数据进行检查。如果您的用户实现AdvancedUserInterface,则会有其他测试:isAccountNonExpired、isAccountNonLocked、isCredentialsNonExpiredAND isEnabled。因此,从数据库加载的用户对于isEnabled返回true,未序列化的用户返回NULL,这是不相等的。根据这个事实,AbstractToken认为您的用户已经更改,并将令牌设置为authenticated=false。

这里有一些来自AbstractToken的代码:

# file vendor/symfony/symfony/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php
<?php
// [...]
namespace Symfony'Component'Security'Core'Authentication'Token;
// [...]
abstract class AbstractToken implements TokenInterface
{
// [...]
    public function setUser($user)
    {
        if (!($user instanceof UserInterface || (is_object($user) && method_exists($user, '__toString')) || is_string($user))) {
            throw new 'InvalidArgumentException('$user must be an instanceof UserInterface, an object implementing a __toString method, or a primitive string.');
        }
        if (null === $this->user) {
            $changed = false;
        } elseif ($this->user instanceof UserInterface) {
            if (!$user instanceof UserInterface) {
                $changed = true;
            } else {
                $changed = $this->hasUserChanged($user);
            }
        } elseif ($user instanceof UserInterface) {
            $changed = true;
        } else {
            $changed = (string) $this->user !== (string) $user;
        }
        if ($changed) {
            // From here the user is not authenticated any more
            $this->setAuthenticated(false);
        }
        $this->user = $user;
    }
// [...]
    private function hasUserChanged(UserInterface $user)
    {
    // [...] Some other checks wich are ok
    if ($this->user instanceof AdvancedUserInterface && $user instanceof AdvancedUserInterface) {
            // [...]
            // Here is the problem with the unserialized user
            if ($this->user->isEnabled() !== $user->isEnabled()) {
                return true;
            }
          // [...]
    }

// [...]
}