2016-05-31 1 views
34

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

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

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

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

  1. аутентифицировать клиент путем создания authentication token
  2. создать новую запись в таблице сеансов в базе данных с сериализованным пользователем
  3. я прохожу созданное печенье в качестве третьего аргумента к connect метод

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

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

+0

Вы только ищите способ передать куки-файл, или вы хотите немедленно отправить, например, sessionId/userId? – mitchken

+0

Я обновил вопрос с помощью кода, который я написал. Проблема в том, что на стороне Ratchet пользователь остается анонимным в тесте. Может быть, я ошибаюсь в печенье. Идентификатор сеанса переносится в файл cookie. – user3746259

+2

''Cookie' => $ symfClient-> getContainer() -> get ('session') -> getId(). знак равно $ symfClient-> getContainer() -> get ('session') -> getId() 'уверен, что это правильно? не должно быть первым, скорее, именем печенья? – bwoebi

ответ

1

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

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

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

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

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

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

Смежные вопросы