Подтвердить что ты не робот

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. Я подумал о чем-то вроде:

  • Аутентификация клиента путем создания токена аутентификации
  • Я создаю новую запись в таблице сеансов в базе данных с сериализованным пользователем
  • Я передаю созданный файл 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("[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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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