Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
596 views
in Technique[技术] by (71.8m points)

php - Token was deauthenticated after trying to refresh it

I updated my Symfony environment form 3.3 to 4.0. After the update I have problems with the login (user provided by database). When I submit the login form, I just got right back to the login form without any error message. When I use invalid credentials, I got the corresponding error message. Here is the log after trying to login. The login with the "in_memory" user provider is working. Do you need more information?

[2017-12-06 13:57:05] security.INFO: User has been authenticated successfully. {"username":"***"} []
[2017-12-06 14:22:39] doctrine.DEBUG: "START TRANSACTION" [] []
[2017-12-06 13:57:05] security.DEBUG: Read existing security token from the session. {"key":"_security_secured_area","token_class":"Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken"} []
[2017-12-06 13:57:05] doctrine.DEBUG: SELECT t0.username AS username_1, t0.password AS password_2, t0.email AS email_3, t0.email_new AS email_new_4, t0.first_name AS first_name_5, t0.last_name AS last_name_6, t0.is_active AS is_active_7, t0.email_confirmed AS email_confirmed_8, t0.shibboleth_state AS shibboleth_state_9, t0.shibboleth_hash AS shibboleth_hash_10, t0.shibboleth_persistent_id AS shibboleth_persistent_id_11, t0.confirmation_email_send AS confirmation_email_send_12, t0.last_login AS last_login_13, t0.expires AS expires_14, t0.session_id AS session_id_15, t0.id AS id_16, t0.hidden AS hidden_17, t0.deleted AS deleted_18, t0.created AS created_19, t0.modified AS modified_20, t0.sorting AS sorting_21, t0.salutation_id AS salutation_id_22, t0.creator_id AS creator_id_23, t0.modifier_id AS modifier_id_24 FROM User t0 WHERE t0.id = ? AND ((t0.deleted = 0)) [2] []
[2017-12-06 13:57:05] security.DEBUG: Token was deauthenticated after trying to refresh it. {"username":"user","provider":"Symfony\Component\Security\Core\User\ChainUserProvider"} []
[2017-12-06 13:57:05] security.INFO: Populated the TokenStorage with an anonymous Token. [] []
[2017-12-06 13:57:05] security.DEBUG: Access denied, the user is not fully authenticated; redirecting to authentication entry point. {"exception":"[object] (Symfony\Component\Security\Core\Exception\AccessDeniedException(code: 403): Access Denied. at /vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/AccessListener.php:68)"} []
[2017-12-06 13:57:05] security.DEBUG: Calling Authentication entry point. [] []

EntityUser:

class User extends EntitySuperclass implements AdvancedUserInterface, Serializable
{
    /**
     * @ORMColumn(type="string")
     */
    private $username;

    /**
     *
     * @AssertLength(max=4096,groups={"account_complete","account_password","user"})
     * @AssertLength(min = 8,groups={"account_complete","account_password","user"}, minMessage="user.password_length")
     */
    private $plainPassword;

    /**
     * The below length depends on the "algorithm" you use for encoding
     * the password, but this works well with bcrypt.
     *
     * @ORMColumn(type="string", length=64)
     */
    private $password;

    /**
     * @ORMColumn(type="string", length=255)
     * @AssertNotBlank(groups={"account_register","user"})
     * @AssertEmail(
     *      groups = {"account_register", "account","user"},
     *      strict = true,
     *      checkMX = true
     * )
     */
    private $email;

    /**
     * @ORMColumn(type="string", length=255)
     */
    private $emailNew = '';

    /**
     * @ORMManyToOne(targetEntity="Salutation")
     * 
     */
    private $salutation;

    /**
     * @ORMColumn(type="string")
     * @AssertNotBlank(groups={"account_complete","user"})
     * @AssertRegex(pattern = "/^[a-zA-Z??ü??ü?0-9 ]+$/",groups={"account_complete","user"}, message="user.first_name.regex")
     */
    private $firstName;

    /**
     * @ORMColumn(type="string")
     * @AssertNotBlank(groups={"account_complete","user"})
     * @AssertRegex(pattern = "/^[a-zA-Z??ü??ü?0-9 ]+$/",groups={"account_complete","user"}, message="user.last_name.regex")
     */
    private $lastName;

    /**
     * @ORMColumn(name="is_active", type="boolean")
     */
    private $isActive = false;

    /**
     * @ORMColumn(name="email_confirmed", type="boolean")
     */
    private $emailConfirmed = false;

    /**
     * @ORMColumn(type="integer")
     */
    private $shibbolethState = 0;

    /**
     * @ORMColumn(type="string")
     */
    private $shibbolethHash = '';

    /**
     * @ORMColumn(type="string")
     */
    private $shibbolethPersistentId = '';

    /**
     * @ORMManyToMany(targetEntity="UserGroup")
     * @ORMJoinTable(name="User_UserGroup",
     *      joinColumns={@ORMJoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORMJoinColumn(name="group_id", referencedColumnName="id")}
     *      )
     */
    private $userGroups;

    /**
     * @ORMColumn(type="integer")
     */
    private $confirmationEmailSend;

    /**
     * @ORMColumn(type="integer")
     */
    private $lastLogin = 0;

    /**
     * @ORMColumn(type="integer")
     */
    protected $expires = 0;

    /**
     * @ORMColumn(type="string", length=255)
     */
    private $sessionId = '';

    /**
     * @ORMManyToMany(targetEntity="BankDetails", cascade={"persist"})
     * @ORMJoinTable(name="User_BankDetails",
     *      joinColumns={@ORMJoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORMJoinColumn(name="bank_details_id", referencedColumnName="id")}
     * )
     * @AssertValid
     */
    private $bankDetails;

    /**
     * @ORMManyToMany(targetEntity="Address", cascade={"persist"})
     * @ORMJoinTable(name="User_BillingAddress",
     *      joinColumns={@ORMJoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORMJoinColumn(name="billing_address_id", referencedColumnName="id")}
     * )
     * @AssertCount(
     *      min = 1,
     *      minMessage = "user.billing_addresses.min",
     * )
     * @AssertValid
     */
    private $billingAddresses;

    public function __construct()
    {
        parent::__construct();
        $this->isActive = true;
        $this->confirmationEmailSend = 0;
        $this->userGroups = new ArrayCollection();
        $this->bankDetails = new ArrayCollection();
        $this->billingAddresses = new ArrayCollection();
        // may not be needed, see section on salt below
        // $this->salt = md5(uniqid(null, true));
    }

    /**
     * @ORMPrePersist
     */
    public function prePersist()
    {
        $currentTimestamp = time();

        if($this->getConfirmationEmailSend() == NULL)
            $this->setConfirmationEmailSend(0);

   }

    public function getUsername()
    {
        //return $this->username;
        return $this->email;
    }

    public function getSalt()
    {
        // The bcrypt algorithm doesn't require a separate salt.
        return null;
    }

    public function getPassword()
    {
        return $this->password;
    }

    public function getRoles()
    {
        $roles = array();
        $userGroups = $this->getUserGroups();
        if(!empty($userGroups)) {
            foreach($userGroups as $userGroup) {
                $role = $userGroup->getRole();
                $roles[] = 'ROLE_'.strtoupper($role);
            }
        }
        return $roles;
    }

    public function isGranted($role)
    {
        return in_array($role, $this->getRoles());
    }

    public function eraseCredentials()
    {
    }

    public function isAccountNonExpired()
    {
        return true;
    }

    public function isAccountNonLocked()
    {
        return true;
    }

    public function isCredentialsNonExpired()
    {
        return true;
    }

    public function isEnabled()
    {
        return $this->isActive;
    }

    /** @see Serializable::serialize() */
    public function serialize()
    {
        return serialize(array(
            $this->id,
            $this->username,
            $this->password,
            $this->isActive,
            // see section on salt below
            // $this->salt,
        ));
    }

    /** @see Serializable::unserialize() */
    public function unserialize($serialized)
    {
        list (
            $this->id,
            $this->username,
            $this->password,
            $this->isActive,
            // see section on salt below
            // $this->salt
        ) = unserialize($serialized);
    }

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

        return $this;
    }

    public function getPlainPassword()
    {
        return $this->plainPassword;
    }

    public function setPlainPassword($password)
    {
        $this->plainPassword = $password;
    }

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

        return $this;
    }

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

        return $this;
    }

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

    /**
     * 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;
    }

    /**
     * Add userGroup
     *
     * @param AppBundleEntityUserGroup $userGroup
     *
     * @return User
     */
    public function addUserGroup(AppBundleEntityUserGroup $userGroup)
    {
        $this->userGroups[] = $userGroup;

        return $this;
    }

    /**
     * Remove userGroup
     *
     * @param AppBundleEntityUserGroup $userGroup
     */
    public function removeUserGroup(AppBundleEntityUserGroup $userGroup)
    {
        $this->userGroups->removeElement($userGroup);
    }

    /**
     * Get userGroups
     *
     * @return DoctrineCommonCollectionsCollection
     */
    public function getUserGroups()
    {
        return $this->userGroups;
    }

    /**
     * Set shibbolethPersistentId
     *
     * @param string $shibbolethPersistentId
     *
     * @return User
     */
    public function setShibbolethPersistentId($shibbolethPersistentId)
    {
        $this->shibbolethPersistentId = $shibbolethPersistentId;

        return $this;
    }

    /**
     * Get shibbolethPersistentId
     *
     * @return string
     */
    public function getShibbolethPersistentId()
    {
        return $this->shibbolethPersistentId;
    }

    /**
     * Set firstName
     *
     * @param string $firstName
     *
     * @return User
     */
    public function setFirstName($firstName)
    {
        $this->firstName = $firstName;

        return $this;
    }

    /**
     * Get firstName
     *
     * @return string
     */
    public function getFirstName()
    {
        return $this->firstName;
    }

    /**
     * Set lastName
     *
     * @param

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

As of Symfony 4.0, logout_on_user_change is set to true. That means a user will be logged out if it has been changed.

You should implement SymfonyComponentSecurityCoreUserEquatableInterface and add the isEqualTo method:

class User implements EquatableInterface
{
    public function isEqualTo(UserInterface $user)
    {
        if ($this->password !== $user->getPassword()) {
            return false;
        }

        if ($this->salt !== $user->getSalt()) {
            return false;
        }

        if ($this->username !== $user->getUsername()) {
            return false;
        }

        return true;
    }
}

Changelog

https://github.com/symfony/security-bundle/blob/master/CHANGELOG.md

4.1.0

The logout_on_user_change firewall option is deprecated and will be removed in 5.0.

4.0.0

the firewall option logout_on_user_change is now always true, which will trigger a logout if the user changes between requests

3.4.0

Added logout_on_user_change to the firewall options. This config item will trigger a logout when the user has changed. Should be set to true to avoid deprecations in the configuration.

The option wasn't documented by the time of writing this answer: https://github.com/symfony/symfony-docs/issues/8428, but it now is: https://symfony.com/doc/4.4/reference/configuration/security.html#logout-on-user-change

Side note on updating to a new major release

If you want to upgrade to a new major version, always update to the latest minor version first. That means update to 2.8 before updating to 3.0 and updating to 3.4 before going to 4.0. See Symfony 4: Compose your Applications by Fabien Potencier.

Symfony 3.0 = Symfony 2.8 - deprecated features

(..)

Symfony 4.0 = Symfony 3.4 - deprecated features + a new way to develop applications

Updating to a new major release is much easier if you're already on the latest minor release, because you can see all deprecation notices.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...