Comment se connecter/authentifier un utilisateur par programme?

Je voudrais 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 réellement.

Voici ma sécurité.yml, je travaille avec deux pare-feu. L'encodeur de texte brut est juste pour tester.

security:
    encoders:
        SymfonyComponentSecurityCoreUserUser: plaintext
        RayCentralBundleEntityClient: 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: RayCentralBundleEntityClient, 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 }
50
demandé sur CSchulz 2012-03-04 01:35:46

3 réponses

Oui, vous pouvez le faire via quelque chose de similaire à ce qui suit:

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 définissez un jeton dans le contexte, alors qu'il le serait normalement lors de l'utilisation 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é, en fonction de votre cas d'utilisation, - la UsernamePasswordToken ci-dessus est une base de jeton, mais vous pouvez utiliser d'autres si nécessaire.

Edit: Ajusté le code ci-dessus pour expliquez le paramètre 'public' et ajoutez également les rôles de l'utilisateur dans la création du jeton, en fonction du commentaire de Franco ci-dessous.

91
répondu richsage 2015-11-01 09:47:56

La version acceptée ne fonctionnera pas avec symfony 3.3. L'Utilisateur sera authentifié dans la requête suivante 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é. La seule façon de contourner cela (hackish comme l'enfer) est de simuler l'existence de la session précédente en initialisant manuellement la session (et le cookie) sur la requête en cours.

Laissez-moi savoir si vous trouvez une meilleure solution.

BTW Je ne suis pas sûr si cela devrait être fusionné avec la solution retenue.

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
répondu pinkeen 2017-07-17 15:18:12

Essayez ceci: pour les utilisateurs de Symfony 3 , n'oubliez pas de faire cette correction pour tester l'égalité des mots de passe (car la méthode montré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

+ d'infos : hash_equals_function

1
répondu Somen Diégo 2017-10-22 17:45:45