Автоматическая аутентификация пользователя после регистрации - PullRequest
107 голосов
/ 04 мая 2011

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

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

Ответы [ 8 ]

140 голосов
/ 25 октября 2013

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 параметра: имя пользователя, учетные данные пользователя, имя брандмауэра, роли пользователя.Вам не нужно указывать учетные данные пользователя, чтобы токен был действительным.

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

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

65 голосов
/ 11 мая 2011

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

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

RegistrationController.php:

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

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

РЕДАКТИРОВАТЬ: Согласно комментарию @ Miquel, я обновил образец кода контроллера, добавив в него разумную роль по умолчанию для нового пользователя (хотя, очевидно, это можно изменить в соответствии с конкретными потребностями вашего приложения).

6 голосов
/ 04 июня 2013

Я использую 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. Кроме того, использование надлежащего поставщика будет означать, что события были обработаны для вас.

6 голосов
/ 24 апреля 2012

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

public function logUser(UserInterface $user) {
    $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
    $this->container->get('security.context')->setToken($token);
}
4 голосов
/ 04 сентября 2012

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

Вызов

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

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

Т.е. вы можете войти в систему только с URL-адреса, находящегося под управлением брандмауэра.

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

2 голосов
/ 03 сентября 2013

Я попробовал все ответы здесь, но никто не работал.Единственный способ аутентификации пользователей на контроллере - это выполнить подзапрос, а затем перенаправить.Вот мой код, я использую 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'));
2 голосов
/ 17 апреля 2012

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

firewalls:
    foobar:
        pattern:    /foo/
1 голос
/ 06 июня 2017

В 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.).
}

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

созданный из FOS \ UserBundle \ Controller \ RegistrationController :: authenticateUser

(из composer.json версия FOSUserBundle: "friendsofsymfony / user-bundle": "~ 1.3")

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...