PHP Websocket在testing中validation用户(传递会话cookie)
我试图testing一个场景,一方面,匿名用户应该立即从WebSocket连接断开连接,另一方面,经过身份validation的用户应该停留在WebSocket连接中。 第一种情况很容易通过使用下面的代码来testing。 身份validation过程不起作用。
对于会话存储,我使用Cookie身份validation与数据库结合使用: Symfony PDO会话存储 。 这一切工作正常,但是当涉及到使用身份validation来testing所描述的行为时,我不知道如何在testing中对用户进行身份validation。 作为一个客户端,我使用PawlasynchronousWebsocket客户端。 这看起来如下:
\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"; });
我知道,作为第三个参数,我可以将头信息传递给“连接”方法,但我无法find一种方式,以便客户端连接,并在ws握手期间cookie正确传递。 我想到了这样的事情:
- 通过创build身份validation令牌来validation客户端
- 我使用序列化用户在数据库的会话表中创build一个新条目
- 我将创build的cookie作为第三个parameter passing给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; } }
作为config_test.yml,我按照以下方式configuration了会话:
session: storage_id: session.storage.mock_file handler_id: session.handler.pdo
对于服务器端websocket的实现,我使用的是Ratchet,它被以下Symfony包所包装: Gos Websocket Bundle
如何在testingwebsockets时authentication用户? 在websocket服务器上,用户总是像“anon-15468850625756b3b424c94871115670”,但是当我手动testing时,他连接正确。
其他问题(二级):如何testing订阅主题? (pubsub)在互联网上没有关于这个的博客条目或任何其他内容。
更新:没有人testing过他们的websockets吗? 这是不重要的,无用的,或为什么任何人都不能帮助这个重要的话题?
你在这里有一个马车前的情况。 当你在一个客户端连接上设置一个cookie时,这个cookie只会在随后的请求(websockets或者XHR,GET,POST等等)上被发送,并提供cookie的限制(httpOnly,secure,domain,path等)匹配。
任何可用的cookie在websocket连接的初始握手期间被发送。 在打开的连接上设置一个cookie会在客户端上设置cookie,但是由于套接字已经是一个打开的连接,并且build立(握手后),服务器将在这个连接期间对这些cookie是盲目的。
有些人在握手期间成功地设置了cookie。 但是,这要求服务器和客户端套接字实现支持此行为并将凭据作为getparameter passing(不良做法)。
所以我认为你唯一真正的select是:
- 在打开websocket 之前通过XHR或其他请求处理authentication
- 使用websocket进行身份validation,但是成功login后:
- 设置您的身份validationcookie
- closures现有的套接字
- 从客户端启动一个新的套接字(然后将携带您的身份validationcookie)
- 完全忘记cookie,并根据打开的连接的请求/资源ID在服务器上处理authentication交换。
如果您select最后一个选项,您仍然可以设置Cookie并查找Cookie以恢复重新连接上的连接。