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

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

Для хранения сеансов я использую аутентификацию Cookie в сочетании с базой данных: Symfony PDO Session Storage . Все работает нормально, но когда дело доходит до тестирования описанного поведения с помощью аутентификации, я не знаю, как аутентифицировать пользователя в тесте. Как клиент, я использую асинхронный клиент Pawl 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. Я подумал о чем-то вроде:

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

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

 // ... 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("testuser@test.com"); $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 

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

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

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

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

У вас есть тележка перед конной ситуацией. Когда вы устанавливаете файл cookie на клиентское соединение, cookie отправляется только на последующие запросы (веб-узлы или XHR, GET, POST и т. Д.), Если ограничения cookie (httpOnly, secure, domain, path и т. Д.) Совпадают.

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

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

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

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

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