web-dev-qa-db-fra.com

Comment se connecter / authentifier par programmation un utilisateur?

Je souhaite connecter l'utilisateur juste après le processus d'inscription, sans passer par le formulaire de connexion.

Est-ce possible ? J'ai trouvé une solution avec FOSUserBundle, mais je ne l'utilise pas sur le projet sur lequel je travaille actuellement.

Voici mon security.yml, je travaille avec deux pare-feu. L'encodeur de texte brut est juste pour les tests.

security:
    encoders:
        Symfony\Component\Security\Core\User\User: plaintext
        Ray\CentralBundle\Entity\Client: md5

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

    providers:
        in_memory:
            users:
                admin: { password: admin, roles: [ 'ROLE_ADMIN' ] }
        entity:
            entity: { class: Ray\CentralBundle\Entity\Client, property: email }

    firewalls:
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false

        user_login:
            pattern:    ^/user/login$
            anonymous:  ~

        admin_login:
            pattern:    ^/admin/login$
            anonymous:  ~

        admin:
            pattern:    ^/admin
            provider: in_memory
            form_login:
                check_path: /admin/login/process
                login_path: /admin/login
                default_target_path: /admin/dashboard
            logout:
                path:   /admin/logout
                target: /

        site:
            pattern:    ^/
            provider: entity
            anonymous:  ~
            form_login:
                check_path: /user/login/process
                login_path: /user/login
                default_target_path: /user
            logout:
                path:   /user/logout
                target: /

    access_control:
        - { path: ^/user/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/user, roles: ROLE_USER }
        - { path: ^/admin, roles: ROLE_ADMIN }
        - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY }
53
rayfranco

Oui, vous pouvez le faire via quelque chose de similaire au suivant:

use Symfony\Component\EventDispatcher\EventDispatcher,
    Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken,
    Symfony\Component\Security\Http\Event\InteractiveLoginEvent;

public function registerAction()
{
    // ...
    if ($this->get("request")->getMethod() == "POST")
    {
        // ... Do any password setting here etc

        $em->persist($user);
        $em->flush();

        // Here, "public" is the name of the firewall in your security.yml
        $token = new UsernamePasswordToken($user, $user->getPassword(), "public", $user->getRoles());

        // For older versions of Symfony, use security.context here
        $this->get("security.token_storage")->setToken($token);

        // Fire the login event
        // Logging the user in above the way we do it doesn't do this automatically
        $event = new InteractiveLoginEvent($request, $token);
        $this->get("event_dispatcher")->dispatch("security.interactive_login", $event);

        // maybe redirect out here
    }
}

Le déclenchement de l'événement à la fin n'est pas automatiquement effectué lorsque vous placez un jeton dans le contexte, alors qu'il le serait normalement lors de l'utilisation, par exemple, d'un formulaire de connexion ou similaire. D'où la raison de l'inclure ici. Vous devrez peut-être ajuster le type de jeton utilisé, selon votre cas d'utilisation - le UsernamePasswordToken illustré ci-dessus est un jeton de base, mais vous pouvez en utiliser d'autres si nécessaire.

Edit : Ajustement du code ci-dessus pour expliquer le paramètre 'public' et également ajouter les rôles de l'utilisateur dans la création du jeton, basé sur le commentaire de Franco ci-dessous .

97
richsage

La version acceptée ne fonctionnera pas avec symfony 3.3. L'utilisateur sera authentifié dans la prochaine requête au lieu de la requête actuelle. La raison en est que ContextListener vérifie l'existence de la session précédente et s'il n'existe pas, il effacera le TokenStorage de sécurité. Le seul moyen de contourner cela (hackish as hell) est de simuler l'existence de la session précédente en initialisant manuellement la session (et le cookie) sur la demande en cours.

Faites-moi savoir si vous trouvez une meilleure solution.

BTW Je ne sais pas si cela devrait être fusionné avec la solution acceptée.

private function logUserIn(User $user)
{
    $token = new UsernamePasswordToken($user, null, "common", $user->getRoles());
    $request = $this->requestStack->getMasterRequest();

    if (!$request->hasPreviousSession()) {
        $request->setSession($this->session);
        $request->getSession()->start();
        $request->cookies->set($request->getSession()->getName(), $request->getSession()->getId());
    }

    $this->tokenStorage->setToken($token);
    $this->session->set('_security_common', serialize($token));

    $event = new InteractiveLoginEvent($this->requestStack->getMasterRequest(), $token);
    $this->eventDispatcher->dispatch("security.interactive_login", $event);
}

Le code ci-dessus suppose que votre nom de pare-feu (ou nom de contexte partagé) est common.

2
pinkeen

Essayez ceci: Pour les utilisateurs de Symfony , n'oubliez pas de faire cette correction pour tester l'égalité des mots de passe (car la méthode indiquée pour tester le mot de passe sur ce lien ne fonctionne pas):

$current_password = $user->getPassword();
$user_entry_password = '_got_from_a_form';

$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($user);
$password = $encoder->encodePassword($user_entry_password, $user->getSalt());

if(hash_equals($current_password, $password)){
//Continue there
}

// I hash the equality process for more security

+ info: hash_equals_function

1
Somen Diégo