Jak programowo logować / uwierzytelniać użytkownika?

Chciałbym zalogować się bezpośrednio po procesie rejestracji, bez przechodzenia przez formularz logowania.

Czy to możliwe ? Znalazłem rozwiązanie FOSUserBundle, ale nie używam go w projekcie, nad którym pracuję. Oto moja Ochrona.yml, pracuję z dwoma firewallami. Koder zwykłego tekstu służy tylko do testowania.
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 }
Author: CSchulz, 2012-03-04

3 answers

Tak, możesz to zrobić za pomocą czegoś podobnego do następującego:

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
    }
}

Wywołanie zdarzenia na końcu nie jest wykonywane automatycznie, gdy ustawisz token w kontekście, podczas gdy normalnie byłoby to przy użyciu np. formularza logowania lub podobnego. Stąd powód włączenia go tutaj. Może być konieczne dostosowanie typu używanego tokenu, w zależności od przypadku użycia - UsernamePasswordToken pokazany powyżej jest tokenem podstawowym, ale w razie potrzeby możesz użyć innych.

Edit : dostosowałem powyższy kod do wyjaśnij parametr "public", a także dodaj role użytkownika do tworzenia tokena, na podstawie poniższego komentarza.

 91
Author: richsage,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-01 09:47:56

Zaakceptowana wersja nie będzie działać z symfony 3.3. Użytkownik zostanie uwierzytelniony w następnym żądaniu zamiast bieżącego. Powodem jest to, że ContextListener sprawdza istnienie poprzedniej sesji, a jeśli nie istnieje, wyczyści security TokenStorage. Jedynym sposobem na obejście tego (hackish as hell) jest sfałszowanie istnienia poprzedniej sesji poprzez ręczne inicjowanie sesji (i pliku cookie) na bieżące żądanie.

Daj znać, jeśli znajdziesz lepsze rozwiązanie.

BTW Nie jestem pewien, czy należy to połączyć z przyjętym rozwiązaniem.

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);
}

Powyższy kod zakłada, że nazwa zapory sieciowej (lub nazwa współdzielonego kontekstu) to common.

 2
Author: pinkeen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-17 15:18:12

Spróbuj tego : dla użytkowników Symfony 3 , nie zapomnij dokonać tej korekty, aby sprawdzić równość haseł (ponieważ metoda pokazana do testowania hasła pod tym linkiem nie działa):

$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
Author: Somen Diégo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-22 17:45:45