简体   繁体   中英

Symfony2 Login SHA512 - Bad Credentials

I've been through literally every SO post regarding this issue but I still can't find my bug. I'm trying to get my login working using sha512. I don't think the password is being encoded correctly, as I've checked on this site . The password I used was "asdf", the salt being generated is "fe739a9eafaff0a5b5091d51e1642a34", and the password stored in my DB is "HzK/fSfJjLQAuAgUhxBzQaPT8cJQ0/05pt5zcYoSM4d7Dxd/WDBiJYXIMmFF70I+". Is this my problem? I simply cannot get past the damned "Bad Credentials" thing. My code is below...

security.yml

security:
    encoders:
        MyBundle\MainBundle\Entity\SystemUser:
            algorithm: sha512
            iterations: 1

    role_hierarchy:
        ROLE_STUDENT:
        ROLE_GUARDIAN:
        ROLE_TEACHER:
        ROLE_SCHOOL_ADMIN:  ROLE_STUDENT, ROLE_GUARDIAN
        ROLE_ADMIN:         ROLE_SCHOOL_ADMIN, ROLE_STUDENT, ROLE_GUARDIAN
        ROLE_SUPER_ADMIN:   [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    providers:
        users:
            entity: { class: MyBundleMainBundle:SystemUser }

    firewalls:
        secured_area:
            pattern: ^/
            anonymous: ~
            form_login:
                login_path: login
                check_path: login_check
                csrf_provider: form.csrf_provider
                csrf_parameter: _csrf_token
                always_use_default_target_path: true
                default_target_path: /dashboard
            logout: true
            anonymous: true

Then, my SystemUser class (sorry it's so long, just want to be comprehensive here)

<?php

namespace MyBundle\MainBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Symfony\Component\Validator\Constraints\Collection;

/**
 * SystemUser
 *
 * @ORM\Table()
 * @ORM\Entity(repositoryClass="MyBundle\MainBundle\Entity\Repository\SystemUserRepository")
 * @ORM\InheritanceType("JOINED")
 * @ORM\DiscriminatorColumn(name="discr", type="integer")
 * @ORM\DiscriminatorMap({"0" = "SystemUser", "1" = "SchoolAdmin", "2" = "Teacher", "3" = "Student", "4" = "Guardian"})
 */
class SystemUser implements AdvancedUserInterface, \Serializable {
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @var string
     *
     * @ORM\Column(type="string", length=50)
     */
    protected $username;

    /**
     * @var string
     *
     * @ORM\Column(type="string", length=255)
     */
    protected $email;

    /**
     * @var string
     *
     * @ORM\Column(type="string", length=32)
     */
    protected $salt;

    /**
     * @var string
     *
     * @ORM\Column(type="string", length=64)
     */
    protected $password;

    /**
     * @var bool
     *
     * @ORM\Column(type="boolean", name="is_active")
     */
    protected $isActive;

    /**
     * @var string
     * @ORM\Column(name="birth_date", type="date")
     */
    protected $birthDate;

    /**
     * @var string
     * @ORM\Column(name="cellphone", type="string", length=10)
     */
    protected $cellphone;

    /**
     * @var ArrayCollection
     * @ORM\ManyToMany(targetEntity="Role", inversedBy="users")
     */
    protected $roles;

    /**
     * @var integer
     * Use this to map to the discr column...
     */
    protected $discr;

    /**
     *
     *
     *
     *
     * Begin methods
     *
     *
     *
     */


    public function __construct() {
        $this->isActive = true;
        $this->salt = md5(uniqid(null, true));
        $this->roles = new ArrayCollection();
    }

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

    /**
     * Set birthDate
     *
     * @param \DateTime $birthDate
     * @return SystemUser
     */
    public function setBirthDate($birthDate)
    {
        $this->birthDate = $birthDate;

        return $this;
    }

    /**
     * Get birthDate
     *
     * @return \DateTime 
     */
    public function getBirthDate()
    {
        return $this->birthDate;
    }

    /**
     * Set cellphone
     *
     * @param string $cellphone
     * @return SystemUser
     */
    public function setCellphone($cellphone)
    {
        $this->cellphone = $cellphone;

        return $this;
    }

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

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

    /**
     * (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) = 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 $this->roles;
    }

    /**
     * Returns the password used to authenticate the user.
     *
     * This should be the encoded password. On authentication, a plain-text
     * password will be salted, encoded, and then compared to this value.
     *
     * @return string The password
     */
    public function getPassword()
    {
        return $this->password;
    }

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

    /**
     * Returns the username used to authenticate the user.
     *
     * @return string The username
     */
    public function getUsername()
    {
        return $this->username;
    }

    /**
     * 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 Boolean 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 Boolean 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 Boolean 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 Boolean true if the user is enabled, false otherwise
     *
     * @see DisabledException
     */
    public function isEnabled()
    {
        return $this->isActive;
    }

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

        return $this;
    }

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

        return $this;
    }

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

    /**
     * Set salt
     *
     * @param string $salt
     * @return SystemUser
     */
    public function setSalt($salt)
    {
        $this->salt = $salt;

        return $this;
    }

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

        return $this;
    }

    /**
     * Set isActive
     *
     * @param boolean $isActive
     * @return SystemUser
     */
    public function setIsActive($isActive)
    {
        $this->isActive = $isActive;

        return $this;
    }

    /**
     * Get isActive
     *
     * @return boolean 
     */
    public function getIsActive()
    {
        return $this->isActive;
    }

    /**
     * Add roles
     *
     * @param \MyBundle\MainBundle\Entity\Role $role
     * @return SystemUser
     */
    public function addRole(\MyBundle\MainBundle\Entity\Role $role)
    {
        $this->roles[] = $role;

        return $this;
    }

    public function removeRole(\MyBundle\MainBundle\Entity\Role $role) {
        $this->roles->removeElement($role);

    }

    /**
     * Get discr
     *
     * @return int
     */
    public function getDiscr() {
        return $this->discr;
    }

    /**
     * Set discr
     *
     * @param $discr
     * @return \MyBundle\MainBundle\Entity\SystemUser
     */
    public function setDiscr($discr) {
        $this->discr = $discr;

        return $this;
    }
}

My SystemUserRepository

class SystemUserRepository extends EntityRepository implements UserProviderInterface
{

    public function loadUserByUsername($username)
    {
        $query = $this->createQueryBuilder('su')
            ->select('su, sr') //SystemUser, SystemRoles
            ->leftJoin('su.roles', 'sr')
            ->where('su.username = :username OR su.email = :email')
            ->setParameter('username', $username)
            ->setParameter('email', $username)
            ->getQuery();

        try {
            $user = $query->getSingleResult();
        } catch (NoResultException $e) {
            $message = 'Unable to find user \'' . $username . '\'';
            throw new UsernameNotFoundException($message, 0, $e);
        }

        return $user;
    }

    public function refreshUser(UserInterface $user)
    {
        $class = get_class($user);
        if (!$this->supportsClass($class)) {
            throw new UnsupportedUserException(
                'Instances of \'' . $class . '\' are not supported'
            );
        }

        return $this->find($user->getId());
    }

    public function supportsClass($class)
    {
        return $this->getEntityName() === $class
                || is_subclass_of($class, $this->getEntityName());
    }
}

and finally my Login

public function loginAction() {
        $request = $this->getRequest();
        $session = $request->getSession();

        if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
            $error = $request->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
        } else {
            $error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
            $session->remove(SecurityContext::AUTHENTICATION_ERROR);
        }

        return $this->render(
            'MyBundleMainBundle:Security:login.html.twig',
            array(
                'last_username' => $session->get(SecurityContext::LAST_USERNAME),
                'error' => $error,
                'csrf_token' => $this->container->get('form.csrf_provider')->generateCsrfToken('authenticate'),
            )
        );
    }

Oh, and if it's of any consequence, my registration controller.

public function createUserAction(Request $request) {
        $entityManager = $this->getDoctrine()->getManager();
        $form = $this->createForm('user_registration', new Registration());
        $form->handleRequest($request);

        if ($form->isValid()) {
            $registration = $form->getData();

            //Handle encoding here...
            $encoderFactory = $this->get('security.encoder_factory');
            $encoder = $encoderFactory->getEncoder($registration->getUser());
            $password = $encoder->encodePassword($registration->getUser()->getPassword(), $registration->getUser()->getSalt());
            $registration->getUser()->setPassword($password);

            $entityManager->persist($registration->getUser());
            $entityManager->flush();

            return $this->redirect($this->generateUrl('dashboard_homepage'));
        }

        return $this->render(
            'MyBundleMainBundle:Security:registration.html.twig',
            array(
                'form' => $form->createView()
            )
        );
    }

Sorry for the long post, hope someone can help me out here! Thanks so much!

change the following code in your SystemUser class:

/**
* @var string
*
* @ORM\Column(type="string", length=64)
*/
protected $password;

to

/**
* @var string
*
* @ORM\Column(type="string", length=255)
*/
protected $password;

I stumbled upon the same problem as you after following the guides on symfony.com. By comparing the results from the hashed password before and after being persisted to the database I could see that the length of the hashed password was 88 characters long, thus it was being truncated to 64 characters after being persisted to the database.

FOSUserBundle is also using a length of 255 on their password field, so I suppose it's a legit change.

I'm guessing you have already solved this problem since it was a while ago you posted it, but i thought I would help others out who, like I did, came here with the same problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM