Как программно войти / аутентифицировать пользователя?

46

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

Возможно ли это? Я нашел решение с FOSUserBundle, но я не использую его в проекте, над которым я действительно работаю.

Вот мой security.yml, я работаю с двумя брандмауэрами. Кодировщик простого текста предназначен только для тестирования.

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 }
  • 0
    Если вы не используете FOSUserBundle, какой пакет вы на самом деле используете?
  • 0
    @hakre Я не использую какой-либо пакет, просто пользовательский объект User, который реализует UserInterface.
Показать ещё 3 комментария
Теги:
authentication
symfony-security

3 ответа

81
Лучший ответ

Да, вы можете сделать это через что-то похожее на следующее:

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

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

Изменить. Скорректированный выше код, чтобы объяснить параметр "public", а также добавить роли пользователя в создание маркера на основе комментария Franco ниже.

  • 0
    Спасибо за этот ответ. Кажется, это правильный путь, но на самом деле это не работает. Ссылаясь на мое последнее редактирование (security.yml), я изменил ключ providerKey (где ваш "public" на "entity"), но я не уверен, что поступил правильно. Когда вы говорите: «Вам может потребоваться настроить тип токена», я не уверен, что понимаю. Я искал здесь Спасибо за вашу помощь.
  • 2
    Я нашел помощь в этой теме и, наконец, нашел, что не так. Третий параметр - это имя брандмауэра, и необходим четвертый параметр, который представляет собой массив ролей для токена. Это сработало для меня
Показать ещё 12 комментариев
2

Принятая версия не будет работать с symfony 3.3. Пользователь будет аутентифицирован в следующем запросе вместо текущего. Причина в том, что ContextListener проверяет существование предыдущего сеанса, и если он не существует, он очистит защиту TokenStorage. Единственный путь вокруг этого (хакерский, как ад) - подделка существования предыдущего сеанса, вручную инициализируя сеанс (и cookie) по текущему запросу.

Сообщите мне, если вы найдете лучшее решение.

Кстати, я не уверен, что это должно быть объединено с принятым решением.

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

В приведенном выше коде предполагается, что ваше имя брандмауэра (или общее имя контекста) common.

  • 0
    Правильный путь - установить для require_previous_session значение false в брандмауэре form_login: require_previous_session: false
  • 0
    Я должен был бы проверить. Но у меня есть смутное воспоминание, что я попробовал это, и это не помогло.
0

Попробуйте следующее: Для пользователей Symfony 3, не забудьте сделать эту поправку, чтобы проверить равенство паролей (как показано на рисунке для проверки пароля по этой ссылке не работает):

$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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню