Автоматическая аутентификация пользователя после регистрации

мы создаем бизнес-приложение с нуля в Symfony 2, и я столкнулся с небольшой проблемой с потоком регистрации пользователя: после того, как пользователь создает учетную запись, они должны автоматически войти в систему с этими учетными данными, а не быть немедленно вынуждены снова предоставить свои учетные данные.

кто-нибудь имел какой-либо опыт в этом, или мог указать мне в правильном направлении?

8 ответов


Symfony 4.0

этот процесс не изменился с symfony 3 на 4, но вот пример использования недавно рекомендованного AbstractController. Оба security.token_storage и session услуги зарегистрированы в Родительском getSubscribedServices метод, поэтому вам не нужно добавлять их в контроллер.

use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use YourNameSpace\UserBundle\Entity\User;

class LoginController extends AbstractController{

    public function registerAction()
    {    
        $user = //Handle getting or creating the user entity likely with a posted form
        $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
        $this->container->get('security.token_storage')->setToken($token);
        $this->container->get('session')->set('_security_main', serialize($token));
        // The user is now logged in, you can redirect or do whatever.
    }

}

Symfony 2.6.x-Symfony 3.0.x

по состоянию на symfony 2.6 security.context Не рекомендуется в пользу security.token_storage. Контроллер теперь может просто:

use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use YourNameSpace\UserBundle\Entity\User;

class LoginController extends Controller{

    public function registerAction()
    {    
        $user = //Handle getting or creating the user entity likely with a posted form
        $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
        $this->get('security.token_storage')->setToken($token);
        $this->get('session')->set('_security_main', serialize($token));
    }

}

пока это устарело, вы все еще можете использовать security.context как это было сделано для обратной совместимости. Просто будьте готовы обновить его для Symfony 3

вы можете прочитать больше об изменениях 2.6 для безопасности здесь:https://github.com/symfony/symfony/blob/2.6/UPGRADE-2.6.md

Symfony 2.3.x

для этого в symfony 2.3 вы больше не можете просто установить токен в безопасности контекст. Также необходимо сохранить токен в сеансе.

предполагая файл безопасности с брандмауэром, как:

// app/config/security.yml
security:
    firewalls:
        main:
            //firewall settings here

и аналогичное действие контроллера:

use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use YourNameSpace\UserBundle\Entity\User;

class LoginController extends Controller{

    public function registerAction()
    {    
        $user = //Handle getting or creating the user entity likely with a posted form
        $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
        $this->get('security.context')->setToken($token);
        $this->get('session')->set('_security_main',serialize($token));
        //Now you can redirect where ever you need and the user will be logged in
    }

}

для создания токена вы захотите создать UsernamePasswordToken, это принимает 4 параметра: сущность пользователя, учетные данные пользователя, имя брандмауэра, роли пользователя. Вам не нужно предоставлять учетные данные пользователя, чтобы токен был действительным.

Im не 100% уверен, что установка токена на the security.context необходимо, если вы просто собираетесь перенаправить сразу. Но, кажется, это не больно, поэтому я оставил его.

затем важная часть, установка переменной сеанса. Соглашение об именовании переменных _security_ с последующим именем брандмауэра, в этом случае main делая _security_main


понял это, наконец.

после регистрации пользователя, вы должны иметь доступ к объекту instanceof все, что вы установили в качестве объекта пользователя в конфигурации поставщика. Решение состоит в том, чтобы создать новый токен с этой сущностью пользователя и передать его в контекст безопасности. Вот пример, основанный на моей настройке:

RegistrationController.php:

$token = new UsernamePasswordToken($userEntity, null, 'main', array('ROLE_USER'));
$this->get('security.context')->setToken($token);

здесь main - это имя брандмауэра для вашего приложения (спасибо, @Joe). Это действительно все есть теперь система считает, что ваш пользователь полностью вошел в систему как пользователь, которого они только что создали.

EDIT: в комментарии @Miquel я обновил образец кода контроллера, чтобы включить разумную роль по умолчанию для нового пользователя (хотя, очевидно, это можно настроить в соответствии с конкретными потребностями вашего приложения).


Если у вас есть объект UserInterface (и это должно быть так большую часть времени), вы можете использовать функцию getRoles, которую она реализует для последнего аргумента. Поэтому, если вы создаете функцию logUser, она должна выглядеть так:

public function logUser(UserInterface $user) {
    $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
    $this->container->get('security.context')->setToken($token);
}

я использую Symfony 2.2, и мой опыт немного отличается от проблематично это, так что это комбинированная версия всей информации из этого вопроса плюс некоторые из моих собственных.

Я думаю Джо неверно о значении $providerKey третий параметр UsernamePasswordToken конструктор. Предполагается, что это ключ поставщика аутентификации (не пользователя). Он используется системой аутентификации для различения токенов, созданных для разных услуги поставщиков. Любой провайдер, который происходит от UserAuthenticationProvider будет аутентифицировать только токены, ключ поставщика которых соответствует его собственному. Например,UsernamePasswordFormAuthenticationListener устанавливает ключ токена, который он создает, чтобы соответствовать ключу соответствующего DaoAuthenticationProvider. Это позволяет одному брандмауэру иметь несколько поставщиков имени пользователя+пароля, не наступая друг на друга. Поэтому нам нужно выбрать ключ, который не будет конфликтовать с другими поставщиками. Я использую 'new_user'.

у меня есть несколько систем в других частях моего приложения, которые зависят от событие успеха аутентификации, и это не срабатывает, просто установив токен в контексте. Я должен был получить EventDispatcher из контейнера и запустите событие вручную. Я решил не стрелять в интерактивный вход в систему событие потому что мы имплицитно аутентификации пользователя, а не в ответ на явный запрос на вход.

use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\AuthenticationEvents;
use Symfony\Component\Security\Core\Event\AuthenticationEvent;

$user = // get a Symfony user instance somehow
$token = new UsernamePasswordToken(
        $user, null, 'new_user', $user->getRoles() );
$this->get( 'security.context' )->setToken( $token );
$this->get( 'event_dispatcher' )->dispatch(
        AuthenticationEvents::AUTHENTICATION_SUCCESS,
        new AuthenticationEvent( $token ) );

обратите внимание, что использование $this->get( .. ) предполагает, что фрагмент находится в методе контроллера. Если вы используете код где-то еще, вам придется изменить их, чтобы вызвать ContainerInterface::get( ... ) в соответствии с требованиями окружающей среды. Как это происходит, мои пользовательские сущности реализуют UserInterface поэтому я могу использовать их непосредственно с токеном. Если ваш нет, вам придется найти способ преобразовать их в UserInterface экземпляров.

этот код работает, но я чувствую, что он взламывает архитектуру аутентификации Symfony, а не работает с он. Вероятно, правильнее было бы реализовать новый поставщик аутентификации со своим собственным классом токенов, а не угон UsernamePasswordToken. Кроме того, использование надлежащего поставщика будет означать, что события были обработаны для вас.


в случае, если у кого-то есть тот же следующий вопрос, который заставил меня вернуться сюда:

вызов

$this->container->get('security.context')->setToken($token); 

действует только текущий security.context для используемого маршрута.

т. е. вы можете войти в систему только с url-адреса в элементе управления брандмауэра.

(при необходимости добавьте исключение для маршрута -IS_AUTHENTICATED_ANONYMOUSLY)


Как проблематично здесь уже упоминалось, этот неуловимый параметр $providerKey на самом деле не что иное, как имя вашего правила брандмауэра, "foobar" в случае приведенного ниже примера.

firewalls:
    foobar:
        pattern:    /foo/

я попробовал все ответы здесь и никто не работал. Единственный способ аутентификации моих пользователей на контроллере-сделать подзапрос, а затем перенаправить. Вот мой код, я использую silex, но вы можете легко адаптировать его к symfony2:

$subRequest = Request::create($app['url_generator']->generate('login_check'), 'POST', array('_username' => $email, '_password' => $password, $request->cookies->all(), array(), $request->server->all());

$response = $app->handle($subRequest, HttpKernelInterface::MASTER_REQUEST, false);

return $app->redirect($app['url_generator']->generate('curriculos.editar'));

на Symfony версии 2.8.11 (вероятно, работает для старых и новых версий),Если вы используете FOSUserBundle просто сделать это :

try {
    $this->container->get('fos_user.security.login_manager')->loginUser(
    $this->container->getParameter('fos_user.firewall_name'), $user, null);
} catch (AccountStatusException $ex) {
    // We simply do not authenticate users which do not pass the user
    // checker (not enabled, expired, etc.).
}

нет необходимости отправлять событие, как я видел в других решениях.

inpired от FOS\UserBundle\Controller\RegistrationController:: authenticateUser

(от композитора.версия json FOSUserBundle: "friendsofsymfony / user-bundle": "~1.3")