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

35

我正在尝试测试一种情境,即一方面匿名用户应立即从Websocket连接中断开,另一方面已认证的用户应该保持在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。我想到了以下方式:

  1. 通过创建身份验证令牌对客户端进行身份验证
  2. 在数据库中创建一个序列化用户的新会话表项
  3. 将创建的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("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):

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

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

在测试websocket时如何验证用户身份?在websocket服务器上,用户始终是类似于“anon-15468850625756b3b424c94871115670”这样的东西,但是当我手动测试时,他会正确连接。

附加问题(次要):如何测试订阅主题?(发布-订阅)互联网上没有博客文章或其他内容。

更新:难道没有人对他们的websocket进行功能测试吗?这很不重要、无用还是为什么没有人在这个重要的话题上提供帮助?


2
“Cookie” => $symfClient->getContainer()->get('session')->getId() . '=' . $symfClient->getContainer()->get('session')->getId() 这个正确吗?第一个不应该是cookie的名称吗? - bwoebi
无论如何,通常你不会测试 WebSocket 本身,而是测试具有处理程序的类,在 Ratchet 的情况下,这将是实现 MessageComponentInterface 接口的类。 - bwoebi
1
@user3746259 好的,我无法猜测出问题出在哪里,所以第一步是直接查看ratchet的websocket代码,收到了什么原始头信息?是否符合您的期望?……调试您的应用程序,直到找到源头。这可能是一个繁琐的任务,但最终您通常会知道为什么它失败了。 - bwoebi
是的,我已经试图查看 ratchet 代码,但无法找到测试中会话 cookie 丢失的位置。我希望这里有人已经使用功能测试测试过 WebSocket,但似乎没有人能提供帮助 :/ - user3746259
1
@user3746259 看起来没有人遇到这个问题。唯一的解决办法就是进行彻底的调试,很抱歉 ;o) - bwoebi
显示剩余5条评论
1个回答

3
您的情况有些本末倒置。当您在客户端建立连接时设置cookie时,只有在后续请求(websockets或XHR、GET、POST等)中的cookie限制(httpOnly、secure、domain、path等)匹配时,该cookie才会被发送过去。
任何可用的cookie都将在websocket连接的初始握手期间发送。在建立的socket连接上设置cookie将在客户端上设置cookie,但由于握手已经完成,服务器将在该连接的持续时间内无法识别这些cookie。
一些人在握手期间成功地设置了cookie。但是,这需要服务器和客户端socket实现支持此行为,并且将凭据作为get参数传递(不良实践)。
因此,我认为您真正的选择只有:
- 在打开websocket之前通过XHR或其他请求处理身份验证。 - 使用websocket进行身份验证,但然后在成功登录后: - 设置您的auth cookie - 关闭现有socket - 从客户端启动一个新的socket(那样就会携带您的auth cookie) - 完全忘记cookie,并根据打开连接的请求/资源ID处理服务器上的身份验证交换。
如果选择最后一个选项,您仍可以设置cookie并查找cookie以恢复重新连接。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接