PHP Websocket аутентифицировать пользователя в тесте (пропустить сеанс cookie)

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

Для хранения сессии я использую аутентификацию Cookie в сочетании с базой данных:Хранилище сессий Symfony PDO, Все работает нормально, но когда дело доходит до тестирования описанного поведения с использованием аутентификации, я не знаю, как аутентифицировать пользователя в тесте. В качестве клиента я используюПаул асинхронный клиент Websocket. Это выглядит следующим образом:

\Ratchet\Client\connect('ws://127.0.0.1:8080')->then(function($conn) {
    $conn->on('message', function($msg) use ($conn) {
        echo "Received: {$msg}\n";
    });

    $conn->send('Hello World!');
}, function ($e) {
    echo "Could not connect: {$e->getMessage()}\n";
});

Я знаю, что в качестве третьего параметра я могу передать информацию заголовка в метод «connect», но не могу найти способ, чтобы клиент подключался и cookie передавался правильно во время рукопожатия ws. Я думал о чем-то вроде:

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

Я думал, что это теория, которая сработает, но пользователь всегда остается анонимным на стороне веб-сокета. Вот код теории пока:

// ...
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class WebsocketTest extends WebTestCase
{

    static $closed;

    protected function setUp()
    {
      self::$closed = null;
    }


    public function testWebsocketConnection()
    {
      $loop = Factory::create();
      $connector = new Connector($loop);

      // This user exists in database user tbl
      $symfClient = $this->createSession("[email protected]");

      $connector('ws://127.0.0.1:80', [], ['Origin' => 'http://127.0.0.1', 'Cookie' => 
                 $symfClient->getContainer()->get('session')->getName() . '=' 
                . $symfClient->getContainer()->get('session')->getId()])
        ->then(function(WebSocket $conn) use($loop){

            $conn->on('close', function($code = null, $reason = null) use($loop) {
                self::$closed = true;
                $loop->stop();
            });
            self::$closed = false;

        }, function(\Exception $e) use ($loop) {
            $this->fail("Websocket connection failed");
            $loop->stop();
        });

      $loop->run();

      // Check, that user stayed logged
      $this->assertFalse(self::$closed);
    }

    private function createSession($email)
    {
      $client = static::createClient();
      $container = $client->getContainer();

      $session = $container->get('session');
      $session->set('logged', true);

      $userManager = $container->get('fos_user.user_manager');
      $em = $container->get('doctrine.orm.entity_manager');
      $loginManager = $container->get('fos_user.security.login_manager');
      $firewallName = 'main';

      $user = $userManager->findUserByEmail($email);

      $loginManager->loginUser($firewallName, $user);

      // save the login token into the session and put it in a cookie
      $container->get('session')->set('_security_' . $firewallName,
        serialize($container->get('security.token_storage')->getToken()));
      $container->get('session')->save();
      $client->getCookieJar()->set(new Cookie($session->getName(), $session->getId()));


      // Create session in database
      $pdo = new PDOSessionStorage();
      $pdo->setSessId($session->getId());
      $pdo->setSessTime(time());
      $pdo->setSessData(serialize($container->get('security.token_storage')->getToken()));
      $pdo->setSessLifetime(1440);

      $em->persist($pdo);
      $em->flush();

      return $client;
  }

}

Как и config_test.yml, я настроил сессию следующим образом:

session:
    storage_id:     session.storage.mock_file
    handler_id:     session.handler.pdo

Для реализации веб-сокета на стороне сервера я использую Ratchet, который упакован в следующий пакет Symfony:Gos Websocket Bundle

Как аутентифицировать пользователя при тестировании веб-сокетов? На сервере веб-сокетов пользователь всегда что-то вроде «anon-15468850625756b3b424c94871115670», но когда я тестирую вручную, он подключается правильно.

Дополнительный вопрос (дополнительный): Как проверить подписку на темы? (pubsub) В интернете нет записей блога или чего-либо еще об этом.

Обновление: никто никогда не проверял функционал своих веб-сокетов? Это неважно, бесполезно или почему никто не может помочь в этой важной теме?

 Curtis Kelsey12 окт. 2016 г., 18:32
@ user3746259 Я успешно использовал Ratchet в модели подписки с аутентификацией на основе токенов (OAuth). Я не пытался использовать аутентификацию на основе файлов cookie. Если вам захочется изменить путь, я мог бы дать краткое изложение этого.
 user374625907 июн. 2016 г., 13:41
Я обновил вопрос с кодом, который я написал. Проблема в том, что на стороне Ratchet пользователь остается анонимным в тесте. Может быть, я передаю печенье неправильно. Идентификатор сеанса передается в файле cookie.
 bwoebi10 авг. 2016 г., 22:10
@ user3746259 Похоже, ни у кого не было проблемы, может быть. Единственное решение - это хардкорная отладка, извините; о)
 bwoebi04 авг. 2016 г., 20:47
В любом случае, обычно вы не тестируете саму websocket, а скорее класс с обработчиками, в случае Ratchets это будут классы, реализующиеMessageComponentInterface
 bwoebi05 авг. 2016 г., 13:07
@ user3746259 Ладно, я не могу точно догадаться, что там происходит, так что, первый шаг, посмотрите непосредственно на код веб-сокета храповика, какие необработанные заголовки получены? Это соответствует вашим ожиданиям? ... Отлаживать приложение, пока не найдете источник. Это может быть утомительным занятием, но в конце вы, как правило, знаете, почему это не удается.
 user374625910 авг. 2016 г., 15:06
Да, я уже пытался изучить храповой код, но не смог найти момент, когда cookie-файл сеанса теряется в тесте. Я надеялся, что кто-то здесь уже тестировал веб-сокет с помощью функционального теста, но, как кажется, никто не может Помогите :/
 user374625912 окт. 2016 г., 22:02
Вместо куки вы используете токен - это звучит хорошо. Не могли бы вы сказать мне, если вы выполните оставшуюся часть теста так же, как я описал выше? Что вы просто передаете токен вместо куки, но вы также создаете сеанс в базе данных и т. Д.?
 bwoebi04 авг. 2016 г., 20:18
'Cookie' => $symfClient->getContainer()->get('session')->getId() . '=' . $symfClient->getContainer()->get('session')->getId() уверен, что это правильно? первым не должно быть имя печенья?
 mitchken06 июн. 2016 г., 18:15
Вы ищете только способ передачи cookie или хотите немедленно отправить, например, sessionId / userId?
 user374625904 авг. 2016 г., 21:34
Вы правы, это была опечатка, когда я создал пример для stackoverflow, но первоначальная проблема заключалась в правильном имени файла cookie. Обычно в веб-приложении я бы написал функциональный тест для аутентификации пользователя во время обычного процесса входа в систему. Так почему бы мне не написать функциональный тест для websocket? То, что вы описываете, было бы интеграционным тестом, который я не ищу. Я "просто" ищу способ аутентификации пользователя в функциональном тесте веб-сокета, поскольку это должно быть возможно каким-то образом: /

Ответы на вопрос(1)

У тебя естьтелега перед лошадью Ситуация здесь. Когда вы устанавливаете cookie для клиентского подключения, этот cookie отправляется толькопоследующий запросы (websockets или XHR, GET, POST и т. д.) при условии, что ограничения cookie (httpOnly, безопасный, домен, путь и т. д.) совпадают.

Все доступные файлы cookie отправляются во время первоначального рукопожатия при подключении к веб-сокету. Установка cookie-файла для открытого соединения установит cookie-файл на клиенте, но, поскольку сокет уже является открытым соединением и установлен (после рукопожатия), сервер будет закрыт для этих куки-файлов в течение всего этого соединения.

Некоторым людям удалось установить cookie во время рукопожатия. Однако для этого требуются реализации сокетов сервера и клиента, поддерживающие это поведение и передающие учетные данные в качестве параметров получения (плохая практика).

Поэтому я думаю, что ваши единственные реальные варианты:

обрабатывать аутентификацию через XHR или другой запросдо открытие веб-розеткииспользуйте websocket для аутентификации, но затем при успешном входе в систему:установите свой файл cookieзакройте существующий сокетинициировать новый сокет от клиента (который будет содержать ваш файл cookie для аутентификации)полностью забыть куки и обработать обмен аутентификацией на сервере на основе идентификатора запроса / ресурса для открытого соединения.

Если вы выберете последний вариант, вы все равно можете установить cookie и искать cookie для восстановления соединений при повторных соединениях.

Ваш ответ на вопрос