PHP Websocket在测试中验证用户(传递会话cookie)

use*_*259 35 php testing websocket symfony ratchet

我试图测试一个场景,一方面,匿名用户应该立即断开与Websocket连接的连接,另一方面,经过身份验证的用户应该保持在websocket连接中.第一种情况可以通过使用下面的代码轻松测试.身份验证过程无效.

对于会话存储,我将Cookie身份验证与数据库结合使用:Symfony PDO会话存储.它一切正常,但是当使用身份验证测试所描述的行为时,我不知道如何在测试中验证用户.作为客户端,我正在使用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";
});
Run Code Online (Sandbox Code Playgroud)

我知道作为第三个参数,我可以将头信息传递给"connect"方法,但我找不到一种方法,以便在ws握手期间连接客户端并正确传递cookie.我想到了类似的东西:

  1. 通过创建身份验证令牌来验证客户端
  2. 我在序列化用户的数据库中的会话表中创建了一个新条目
  3. 我将创建的cookie作为第三个参数传递给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("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;
  }

}
Run Code Online (Sandbox Code Playgroud)

作为config_test.yml,我按以下方式配置了会话:

session:
    storage_id:     session.storage.mock_file
    handler_id:     session.handler.pdo
Run Code Online (Sandbox Code Playgroud)

对于服务器端websocket实现,我使用的是Ratchet,它由以下Symfony包裹包装:Gos Websocket Bundle

如何在测试websockets时对用户进行身份验证?在websocket服务器上,用户总是像"anon-15468850625756b3b424c94871115670",但是当我手动测试时,他连接正确.

附加问题(二级):如何测试主题订阅?(pubsub)互联网上没有关于此的博客条目或任何其他内容.

更新:没有人功能测试他们的websockets?这不重要,没用,或者为什么没有人能帮助解决这个重要的话题?

小智 3

这里有本末倒置的情况。当您在客户端连接上设置 cookie 时,只要cookie 限制(httpOnly、secure、domain、path 等)匹配,该 cookie只会在后续请求(websockets 或 XHR、GET、POST 等)中发送。

任何可用的 cookie 都会在 Websocket 连接的初始握手期间发送。在打开的连接上设置 cookie 会在客户端上设置 cookie,但由于套接字已经是打开的连接并已建立(握手后),因此服务器在该连接期间将看不到这些 cookie。

有些人在握手期间成功设置了 cookie。但是,这需要服务器和客户端套接字实现支持此行为并将凭据作为获取参数传递(不好的做法)。

所以我认为你唯一真正的选择是:

  • 在打开 websocket之前通过 XHR 或其他请求处理身份验证
  • 使用 websocket 进行身份验证,但在成功登录后:
    • 设置您的身份验证 cookie
    • 关闭现有套接字
    • 从客户端启动一个新的套接字(然后它将携带您的身份验证 cookie)
  • 完全忘记 cookie,并根据打开连接的请求/资源 ID 在服务器上处理身份验证交换。

如果您选择最后一个选项,您仍然可以设置 cookie 并查找 cookie 以在重新连接时恢复连接。