35 votes

PHP Websocket authentifie l'utilisateur dans un test (passe le cookie de session)

Je suis en train de tester un scénario, que, d'une part, les utilisateurs anonymes doivent obtenir immédiatement un déconnecter une connexion Websocket et, d'autre part, les utilisateurs authentifiés doivent rester dans la connexion websocket. Le premier cas est facilement vérifiable en utilisant le code down under. Le processus d'authentification ne fonctionne pas.

Pour le stockage de session, je suis à l'aide de l'authentification par Cookie en combinaison avec une base de données: Symfony AOP de Stockage de Session. Ça fonctionne bien, mais quand il s'agit de tester le comportement décrit par l'utilisation de l'authentification, je ne sais pas comment authentifier l'utilisateur dans un test. En tant que client, je suis à l'aide de Cliquet asynchrone client Websocket. Cela ressemble à la suivante:

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

Je sais que d'un troisième paramètre, je peux passer des informations d'en-tête de la "connexion" de la méthode, mais je ne peux pas trouver un moyen pour que le client est connecté et le cookie est transmis correctement au cours de la ws poignée de main. J'ai pensé à quelque chose comme:

  1. Authentifier un client par la création d'un jeton d'authentification
  2. J'ai créer une nouvelle entrée dans la table de session dans la base de données avec un utilisateur
  3. Je passe le cookie créé en tant que troisième argument de la méthode de connexion,

C'est la théorie que je pensais qui serait à l'œuvre, mais l'utilisateur reste toujours anonyme sur websocket côté. Voici le code de la théorie de la mesure:

// ...
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;
  }

}

Comme config_test.yml, j'ai configuré la session de la manière suivante:

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

Pour le côté serveur websocket mise en œuvre, je suis en utilisant la clé à Cliquet, qui est d'être enveloppé par la suite bundle Symfony: Gos Websocket Bundle

Comment authentifier l'utilisateur lors de l'essai de websockets? Sur le serveur websocket, l'utilisateur est toujours quelque chose comme "anon-15468850625756b3b424c94871115670", mais quand j'ai tester manuellement, il est connecté correcte.

Question supplémentaire (secondaire): Comment tester l'abonnement à des sujets? (pubsub) Il n'y a pas des entrées de blog ou quoi que ce soit d'autre à ce sujet sur l'internet.

Mise à jour: personne N'a jamais fonctionnel testé leur websockets? Est-ce sans importance, inutiles ou pourquoi ne peut-on aider sur ce sujet important?

3voto

xcsrz Points 21

Vous avez un chariot avant le cheval de la situation ici. Lorsque vous définissez un cookie sur un client de connexion que cookie est envoyé sur subséquente des demandes (websockets ou XHR, GET, POST, etc...) à condition que le cookie restrictions (httpOnly, sécurisé, de domaine, de chemin d'accès, etc) sont identiques.

Tout cookies sont envoyés au cours de la première poignée de main de la connexion websocket. La définition d'un cookie sur une connexion ouverte permettra de définir le cookie sur le client, mais depuis le socket est déjà une connexion ouverte et a établi post (poignée de main), le serveur sera aveugle de ces témoins, pour la durée de la connexion.

Certaines personnes ont eu du succès en paramètre le cookie lors de la poignée de main. Cependant, cela nécessite que le serveur et le client socket implémentations de soutenir ce comportement et de la transmission des informations d'identification que d'obtenir des paramètres (mauvaise pratique).

Donc, je pense que la seule vraie options sont les suivantes:

  • gérer l'authentification via XHR ou de toute autre demande avant l'ouverture d'un websocket
  • utiliser le websocket pour l'authentification, mais alors sur le succès de la connexion:
    • définissez votre auth cookie
    • fermer la socket existant
    • lancer un nouveau socket du client (qui va alors effectuer votre auth cookie)
  • oubliez complètement les cookies et gérer un échange d'authentification sur le serveur en fonction de la demande/ID de ressource pour la connexion ouverte.

Si vous choisissez la dernière option, vous pouvez toujours définir le cookie et regardez pour le cookie de restaurer des connexions sur se reconnecte.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X