简体   繁体   中英

Symfony 3 - How to authenticate a user from a password encrypted by bcrypt?

I have a User entity, with password encryption by bcrypt, in the database.

In fact, when a user is created, I generate a random password of 7 characters, and send him by mail. This password is encrypted using bcrypt and then in the database in the password attribute.

I would like that when my user connects from my login form, he can enter the password that I sent him by email to authenticate. But I do not understand how to do it. I can look at different sites, I do not see.

In addition, I think my code is a little mess ... My ultimate goal being to authenticate the user, so he has an open session, with a possibility to disconnect, and it is restricted to certain pages. But also to manage if the user is activated, and its expiration date.

I emphasize the fact that I am a beginner in symfony (I discovered it 2 weeks ago as part of my internship, and I knew very little PHP, so it's quite difficult for me)

I do not use the fosUserBundle bundle.

This is my code :

User.php :

<?php

namespace Site\PagesBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;


/**
 * User
 *
 * @ORM\Table(name="user")
 * @ORM\Entity(repositoryClass="Site\PagesBundle\Repository\UserRepository")
 */
class User implements UserInterface, \Serializable
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="username", type="string", length=255, nullable=true, unique=true)
     */
    private $username;


    /**
     * @var string
     *
     * @ORM\Column(name="nom", type="string", length=255)
     */
    private $nom;

       /**
     * @var string
     *
     * @ORM\Column(name="prenom", type="string", length=255)
     */
    private $prenom;



    /**
     * @var string
     *
     * @ORM\Column(name="email", type="string", length=254, unique=true)
     */
    private $email;

     /**
     * @Assert\Length(max=4096)
     */
    private $plainPassword;

    /**
     * @var string
     *
     * @ORM\Column(name="password", type="string", length=64, nullable=true)
     */
    private $password;

    /**
     * @var int
     *
     * @ORM\Column(name="nbTelechargementsAuto", type="integer", nullable=true)
     */
    private $nbTelechargementsAuto;

    /**
     * @var bool
     *
     * @ORM\Column(name="isActif", type="boolean")
     */
    private $isActif=0;

    /**
     * @ORM\Column(type="datetime", nullable=true)
     *
     * @var \DateTime
    */
    private $createdAt;


    /**
     * @var bool
     *
     * @ORM\Column(name="isCreated", type="boolean")
     */
    private $isCreated=0;

    /**
     * @ORM\Column(type="array")
     */
    private $roles;

    /**
     * Set createdAt
     *
     * @param \DateTime $createdAt
     *
     * @return User
     */
    public function setCreatedAt()
    {
        $this->createdAt = new \DateTimeImmutable();

        return $this;
    }

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


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

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

        return $this;
    }

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

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

        return $this;
    }

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

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

        return $this;
    }

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

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

        return $this;
    }

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

    /**
     * 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 nbTelechargementsAuto
     *
     * @param integer $nbTelechargementsAuto
     *
     * @return User
     */
    public function setNbTelechargementsAuto($nbTelechargementsAuto)
    {
        $this->nbTelechargementsAuto = $nbTelechargementsAuto;

        return $this;
    }

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

    /**
     * Set isActif
     *
     * @param boolean $isActif
     *
     * @return User
     */
    public function setIsActif($isActif)
    {
        $this->isActif = $isActif;

        return $this;
    }

    /**
     * Get isActif
     *
     * @return bool
     */
    public function getIsActif()
    {
        return $this->isActif;
    }


        /**
     * Set isCreated
     *
     * @param boolean $isCreated
     *
     * @return User
     */
    public function setIsCreated($isCreated)
    {
        $this->isCreated = $isCreated;

        return $this;
    }

    /**
     * Get isCreated
     *
     * @return bool
     */
    public function getIsCreated()
    {
        return $this->isCreated;
    }

    public function __construct()
    {
        $this->roles = ['ROLE_USER'];
    }

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

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

    public function getSalt()
    {
        // you *may* need a real salt depending on your encoder
        // see section on salt below
        return null;
    }

    public function getRoles()
    {
        return $this->roles;
    }

    public function eraseCredentials()
    {
    }

    /** @see \Serializable::serialize() */
    public function serialize()
    {
        return serialize([
            $this->id,
            $this->username,
            $this->password,
            $this->email,
            $this->nbTelechargementsAuto,
            $this->nom,
            $this->prenom,
            // see section on salt below
            // $this->salt,
        ]);
    }

    /** @see \Serializable::unserialize() */
    public function unserialize($serialized)
    {
        list (
            $this->id,
            $this->username,
            $this->password,
            $this->email,
            $this->nbTelechargementsAuto,
            $this->nom,
            $this->prenom,
            // see section on salt below
            // $this->salt
        ) = unserialize($serialized, ['allowed_classes' => false]);
    }


    /**
     * Activation du compte
     */
    public function activerCompte($nbPackages, $nbHomonymes)
    {

        if($this->getIsCreated() == 0)
        {
            $unUsername = $this->getNom();
            $unUsername .= ".";
            $unUsername .= $this->getPrenom();
            $unUsername = strtolower($unUsername);

            if($nbHomonymes > 0)
            {
                $nbHomonymes++;
                $unUsername .= $nbHomonymes;
            }


            $this->setUsername(strtolower($unUsername));
            $this->setNbTelechargementsAuto($nbPackages);
            $this->setCreatedAt();
            $this->setIsCreated(true);

        }

        $password = $this->generatePassword();

        $this->setPlainPassword($password);
        $this->setIsActif(true);

        return $this;
    }

    public function constructionUsername()
    {
        $unUsername = $this->getNom();
        $unUsername .= ".";
        $unUsername .=$this->getPrenom();

        return $unUsername;
    }

    /**
     * Désactivation du compte
     */
    public function desactiverCompte()
    {
        $this->setIsActif(false);
        $this->setCreatedAt();

        return $this;
    }

    public function registration()
    {
        // Passage Nom 1ère lettre Majuscule le reste minuscule
        $leNom = $this->getNom();
        $leNom = strtolower($leNom);
        $leNom = ucfirst($leNom);

        // Passage Nom 1ère lettre Majuscule le reste minuscule
        $lePrenom = $this->getPrenom();
        $lePrenom = strtolower($lePrenom);
        $lePrenom = ucfirst($lePrenom);

        $this->setNom($leNom);
        $this->setPrenom($lePrenom);
        $this->setCreatedAt();
    }

    /**
     * Génération d'un mot de passe
     * $nb_caractere = nombre de caractère du mdp
     * Pas de caractère qui se ressemble (L minuscule, 1 et i majuscule, Zéro et la lettre o.)
     * Sécurité supplémentaire avec : caractères speciaux: - + = $ % ! @ #
     * Pas d'espace pour éviter les erreurs
     */
    function generatePassword($nb_caractere = 7)
{
        $mot_de_passe = "";

        $chaine = "abcdefghjkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ023456789@!$?&";
        $longeur_chaine = strlen($chaine);

        for($i = 1; $i <= $nb_caractere; $i++)
        {
            $place_aleatoire = mt_rand(0,($longeur_chaine-1));
            $mot_de_passe .= $chaine[$place_aleatoire];
        }

        return $mot_de_passe;   
}


}

config.php:

imports:
    - { resource: parameters.yml }
    - { resource: security.yml }
    - { resource: services.yml }

# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters:
    locale: en

framework:
    # ...
    templating:
        engines: ['twig']
    #esi: ~
    #translator: { fallbacks: ['%locale%'] }
    secret: '%secret%'
    router:
        resource: '%kernel.project_dir%/app/config/routing.yml'
        strict_requirements: ~
    form: ~
    csrf_protection: ~
    validation: { enable_annotations: true }
    #serializer: { enable_annotations: true }
    default_locale: '%locale%'
    trusted_hosts: ~
    session:
        # https://symfony.com/doc/current/reference/configuration/framework.html#handler-id
        handler_id: session.handler.native_file
        save_path: '%kernel.project_dir%/var/sessions/%kernel.environment%'
    fragments: ~
    http_method_override: true
    assets: ~
    php_errors:
        log: true

# Twig Configuration
twig:
    debug: '%kernel.debug%'
    strict_variables: '%kernel.debug%'
    form_themes: ['bootstrap_4_layout.html.twig']


# Doctrine Configuration
doctrine:
    dbal:
        driver: pdo_mysql
        host: '%database_host%'
        port: '%database_port%'
        dbname: '%database_name%'
        user: '%database_user%'
        password: '%database_password%'
        charset: UTF8
        # if using pdo_sqlite as your database driver:
        #   1. add the path in parameters.yml
        #     e.g. database_path: '%kernel.project_dir%/var/data/data.sqlite'
        #   2. Uncomment database_path in parameters.yml.dist
        #   3. Uncomment next line:
        #path: '%database_path%'

    orm:
        auto_generate_proxy_classes: '%kernel.debug%'
        naming_strategy: doctrine.orm.naming_strategy.underscore
        auto_mapping: true

# Swiftmailer Configuration
swiftmailer:
    transport: '%mailer_transport%'
    host: '%mailer_host%'
    username: '%mailer_user%'
    password: '%mailer_password%'
    spool: { type: memory }

sensio_framework_extra:
   router:
        annotations: false

assetic:
    debug:          "%kernel.debug%"
    use_controller: false
    bundles:    [ ]
   #java: /usr/bin/java
    java: C:\Program Files\Java\jdk1.8.0_65\bin\java.exe
    filters:
        cssrewrite: ~
        cssembed:
            jar: "%kernel.root_dir%/Resources/java/cssembed.jar"        
            yui_js:
            jar: "%kernel.root_dir%/Resources/java/yuicompressor.jar"
        yui_css:
            jar: "%kernel.root_dir%/Resources/java/yuicompressor.jar"
        lessphp:
            file: "%kernel.root_dir%/../vendor/oyejorge/less.php/lessc.inc.php"   
            apply_to: ".less$"
    assets:
        jquery_js:
            inputs:
                - "%kernel.root_dir%/../vendor/components/jquery/jquery.min.js"            
            filters: [?yui_js]
            output: js/jquery.min.js

        bootstrap_css:
            inputs:
                - "%kernel.root_dir%/../vendor/twbs/bootstrap/less/bootstrap.less"
            filters:
                - lessphp
                - cssrewrite
            output: css/bootstrap.css            

        bootstrap_js:
            inputs:
                - "%kernel.root_dir%/../vendor/twbs/bootstrap/js/affix.js"
                - "%kernel.root_dir%/../vendor/twbs/bootstrap/js/alert.js"
                - "%kernel.root_dir%/../vendor/twbs/bootstrap/js/button.js"
                - "%kernel.root_dir%/../vendor/twbs/bootstrap/js/carousel.js"
                - "%kernel.root_dir%/../vendor/twbs/bootstrap/js/collapse.js"
                - "%kernel.root_dir%/../vendor/twbs/bootstrap/js/dropdown.js"
                - "%kernel.root_dir%/../vendor/twbs/bootstrap/js/modal.js"
                - "%kernel.root_dir%/../vendor/twbs/bootstrap/js/tooltip.js"
                - "%kernel.root_dir%/../vendor/twbs/bootstrap/js/popover.js"
                - "%kernel.root_dir%/../vendor/twbs/bootstrap/js/scrollspy.js"
                - "%kernel.root_dir%/../vendor/twbs/bootstrap/js/tab.js"
                - "%kernel.root_dir%/../vendor/twbs/bootstrap/js/transition.js"
            filters: [?yui_js]
            output: js/bootstrap.js             
        fonts_glyphicons_eot:
            inputs:
                - "%kernel.root_dir%/../vendor/twbs/bootstrap/fonts/glyphicons-halflings-regular.eot"
            output: "fonts/glyphicons-halflings-regular.eot"
        fonts_glyphicons_svg:
            inputs:
                - "%kernel.root_dir%/../vendor/twbs/bootstrap/fonts/glyphicons-halflings-regular.svg"
            output: "fonts/glyphicons-halflings-regular.svg"
        fonts_glyphicons_ttf:
            inputs:
                - "%kernel.root_dir%/../vendor/twbs/bootstrap/fonts/glyphicons-halflings-regular.ttf"
            output: "fonts/glyphicons-halflings-regular.ttf"
        fonts_glyphicons_woff:
            inputs:
                - "%kernel.root_dir%/../vendor/twbs/bootstrap/fonts/glyphicons-halflings-regular.woff"

            output: "fonts/glyphicons-halflings-regular.woff"

stof_doctrine_extensions: 
    orm: 
        default: 
            sluggable: true

vich_uploader:
    db_driver: orm
    twig: true
    storage: file_system

    mappings:
        paquet:
            uri_prefix: fichiers/packages
            upload_destination: '%kernel.root_dir%/../web/fichiers/packages/' 

            inject_on_load: true
            delete_on_update: true
            delete_on_remove: true

        notice:
            uri_prefix: fichiers/notices
            upload_destination: '%kernel.root_dir%/../web/fichiers/notices/' 

            inject_on_load: false
            delete_on_update: true
            delete_on_remove: true

fos_ck_editor:
    configs:
        my_config:
            toolbar: [ ["Source", "-", "Save"], "/", ["Anchor"], "/", ["Maximize"] ]
            uiColor:                "#000000"

Security.yml :

# To get started with security, check out the documentation:
# https://symfony.com/doc/current/security.html
security:
    encoders:
        Site\PagesBundle\Entity\User: bcrypt

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

    # https://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
    providers:
        in_memory: {memory: ~}
        in_database:
            entity:
                class: Site\PagesBundle\Entity\User
                property: username

        our_db_provider:
            entity:
                class: Site\PagesBundle\Entity\User
                property: username
                # if you're using multiple entity managers
                # manager_name: customer

    firewalls:
        # disables authentication for assets and the profiler, adapt it according to your needs
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        main:
            anonymous: true

            provider: in_database

            form_login:
                login_path: security_login
                check_path: security_login

            logout:
                path: security_logout
                target: informations

            # activate different ways to authenticate

            # https://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate
            #http_basic: ~

            # https://symfony.com/doc/current/security/form_login_setup.html
            #form_login: ~

And a part fo the controller that I use to manage what is relative to the navigation of the user, including its authentication

DefaultController.php :

<?php

namespace Site\PagesBundle\Controller;

use Site\PagesBundle\Entity\User;
use Site\PagesBundle\Entity\Paquet;
use Site\PagesBundle\Entity\TypeUser;
use Site\PagesBundle\Entity\Information;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Vich\UploaderBundle\Handler\DownloadHandler;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;

/**
 * Default controller.
 *
 * @Route("accueil")
 */
class DefaultController extends Controller
{

    /**
     * Accueil
     *
     * @Route("/", name="connexion_index")
     * @Method({"GET", "POST"})
     */
    public function indexAction(Request $request, UserPasswordEncoderInterface $passwordEncoder)
    {

        $em = $this->getDoctrine()->getManager(); //Récupération du manager

        $listeInfos = $em->getRepository('PagesBundle:Information')->getInformationsZone("Zone 1"); //Récupération d'une liste d'informations

        $user = new User(); //Initialisation de l'objet User
        $form = $this->createForm('Site\PagesBundle\Form\ConnexionType', $user); //Formulaire de création
        $form->handleRequest($request);

        //Traitement si le formulaire est soumis ( Ajout du package dans la BDD )
        if ($form->isSubmitted() && $form->isValid()) {
            $em = $this->getDoctrine()->getManager();
            $em->persist($user);

            $password = $passwordEncoder->encodePassword($user, $user->getPlainPassword());

            $valide = $this->getDoctrine()->getManager()->getRepository('PagesBundle:User')->authentifier($user->getUsername(),$password);
            dump($valide);
            dump($password);

            if($valide == 1)
            {
                return $this->redirectToRoute('accueil');
            }

            else
            {
                return $this->render('@Pages/Default/connexion.html.twig',array(
                    'user' => $user,
                    'form' => $form->createView(),
                    'listeInfos' => $listeInfos,
                ));
            }

            // On ajoute un package, donc on offre un téléchargement supplémentaire aux utilisateurs concernés
            $this->getDoctrine()->getManager()->getRepository('PagesBundle:User')->updateNbDDLAll("inc"); 

            //return $this->redirectToRoute('paquets_index'); // Redirection page de gestion de packages
        }



        return $this->render('@Pages/Default/connexion.html.twig',array(
            'user' => $user,
            'form' => $form->createView(),
            'listeInfos' => $listeInfos,
        ));
    }

Thanks for your help !

EDIT:

My function :

 /**
     * Accueil
     *
     * @Route("/", name="connexion_index")
     * @Method({"GET", "POST"})
     */
    public function indexAction(Request $request, UserPasswordEncoderInterface $passwordEncoder)
    {

        $em = $this->getDoctrine()->getManager(); //Récupération du manager

        $listeInfos = $em->getRepository('PagesBundle:Information')->getInformationsZone("Zone 1"); //Récupération d'une liste d'informations

        $user = new User(); //Initialisation de l'objet User
        $form = $this->createForm('Site\PagesBundle\Form\ConnexionType', $user); //Formulaire de création
        $form->handleRequest($request);

        //Traitement si le formulaire est soumis ( Ajout du package dans la BDD )
        if ($form->isSubmitted() && $form->isValid()) {
            $em = $this->getDoctrine()->getManager();
            $em->persist($user);

            $password = $passwordEncoder->encodePassword($user, $user->getPlainPassword());

            $valide = $this->getDoctrine()->getManager()->getRepository('PagesBundle:User')->authentifier($user->getUsername(),$password);
            dump($valide);
            dump($password);

            if($valide == 1)
            {
                return $this->redirectToRoute('accueil');
            }

            else
            {
                return $this->render('@Pages/Default/connexion.html.twig',array(
                    'user' => $user,
                    'form' => $form->createView(),
                    'listeInfos' => $listeInfos,
                ));
            }

            // On ajoute un package, donc on offre un téléchargement supplémentaire aux utilisateurs concernés
            $this->getDoctrine()->getManager()->getRepository('PagesBundle:User')->updateNbDDLAll("inc"); 

            //return $this->redirectToRoute('paquets_index'); // Redirection page de gestion de packages
        }

I retrieve the password entered by the user in the login form. I encrypt it. Then I run a check in my UserRepository with the username and password encrypted. If the function returns true, there is someone who has the username entered and the encrypted password entered.

My UserRepository function :

public function authentifier($username, $password)
    {
        $queryBuilder = $this->createQueryBuilder("u")
        ->select("count(u.id)")
        ->where("u.username = :username")
        ->andWhere("u.password = :password")
        ->setParameter("username",$username)
        ->setParameter("password",$password);
        return $queryBuilder->getQuery()->getSingleScalarResult();
    }
}

However, what is the cost in the security.yml?

To check if the posted password on your login form match with the one in database, you just need to bcrypt it and check if it's the same as the one in db.

  • You get your User from db
  • you encode the posted password
  • You check if the posted encoded password is the same as the one sotred in your User

But in your Controller code, it seems you create a new user (you create a new User instrad of getting one from db, and you persist it) ?

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