Node.js Express, Socket.IO e Sessions

Publicação: | Tags: Node.js

Compartilhando Session entre Express e Socket.IO

Atenção

Esse post esta utilizando versões antigas do Express e Socket.IO, caso queira aplicar essa técnica nas versões Express 4 e Socket.IO 1.0, recomendo que leiam esse post: Criando um chat usando session do Express 4 no Socket.IO 1.0.

Irei compartilhar uma dica muito útil, pelo qual levei muitos dias quebrando a cabeça para fazer essa técnica funcionar! Se você utiliza Socket.IO e Express juntos, esse tutorial vai te ajudar a manter mais seguro o seu sistema, compartilhando Sessions do Express com Socket.IO ao invés de aderir a gambiarras soluções paliativas.

O Socket.IO consegue acessar e manipular Sessions que forem criadas por um servidor web (neste caso o Express). E o que faremos é um controle de session compartilhada.

Quais as vantagens disso?

É mais seguro acessar os dados de uma Session compartilhada, do que enviar tais dados das Sessions para o HTML e depois reenviá-los para o Socket.IO, isso por que código no cliente pode ser facilmente adulterado, até mesmo usando um Firebug ou Chrome DevTools é possível alterar os valores de código HTML ou Javascript.

Como isso funciona?

Na prática, quando entramos no sistema, o Express automaticamente cria uma SessionID. Essa variável pode ser persistida em memória ou em disco no servidor (essa decisão fica a critério dos desenvolvedores, por default o Express mantém em memória). O Socket.IO não consegue acessar esses dados, ele apenas possui um controle para autorizar uma conexão do cliente io.set('authorization');, com isso podemos utilizar as funções de Session, MemoryStore e cookieParser do Express dentro do autorizador Socket.IO, com isso buscamos e validamos uma Session, se o mesmo for válido, armazenamos no cliente Socket.IO e liberamos sua conexão no sistema.

Obs.: Estou utilizando as últimas versões do Socket.IO e Express, não vai funcionar essa dica em versões anteriores. O código abaixo vai explicar de forma mais prática:

 // app.js
 const KEY = 'express.sid'
   , SECRET = 'express';
 var express = require('express')
   , app = express()
   , server = require('http').createServer(app)
   , io = require('socket.io').listen(server)
   , cookie = express.cookieParser(SECRET)
   , store = new express.session.MemoryStore()
   , session = express.session({secret: SECRET
                              , key: KEY
                              , store: store});
 // Configurações de Cookie e Session do Express
 app.configure(function(){
   app.set('view engine', 'ejs');
   app.use(cookie);
   app.use(session);
 });
 app.get("/", function(req, res){
   req.session.nome = "Caio";
   res.render('index');
 });
 server.listen(3000, function(){
   console.log("Express e Socket.IO no ar.");
 });
 // Configurações do Socket.IO
 io.set('authorization', function(data, accept) {
   cookie(data, {}, function(err) {
     if (!err) {
       var sessionID = data.signedCookies[KEY];
       store.get(sessionID, function(err, session) {
         if (err || !session) {
           accept(null, false);
         } else {
           data.session = session;
           accept(null, true);
         }
       });
     } else {
       accept(null, false);
     }
   });
 });
 io.sockets.on('connection', function (client) {
   var session = client.handshake.session
     , nome = session.nome;
   client.on('toServer', function (msg) {
     msg = "<b>"+nome+":</b> "+msg+"<br>";
     client.emit('toClient', msg);
     client.broadcast.emit('toClient', msg);
   });
 });

Com esses recursos habilitado será possível utilizar session no Socket.IO, em io.set('authorization'); temos um callback accept(), ele é responsável pela autorização da conexão e a variável data contém informações do cliente, isso inclui headers, cookies e outras informações do HTTP. Buscamos o sessionID através da variável data.signedCookies[KEY], em seguida buscamos os dados da session que estão na memória do servidor através da função store.get(), se tudo ocorrer com sucesso, incluimos a session na variável data e liberamos a conexão pela função accept(null, true). Será através do evento io.sockets.on('connection') que acessamos a session via client.handshake.session.

Pronto! Agora o Socket.IO esta habilitado para ler e manipular os objetos de uma session criada pelo Express. Com isso, podemos trafegar os dados do usuário logado dentro do nosso chat. Para finalizar, implementaremos a view carregando um simples chat que envia apenas mensagem digitada:

 <html>
   <body>
     <script src="/socket.io/socket.io.js"></script>
     <script>
       var socket = io.connect('http://localhost:3000');
         socket.on('toClient', function (msg) {
         var chat = document.getElementById('chat');
         chat.innerHTML += msg;
       });
       var enviar = function() {
         var msg = document.getElementById('msg');
         socket.emit('toServer', msg.value);
       };
     </script>
     <section>
       <pre id="chat"></pre>
       <input type="text" id="msg" placeholder="Digite sua mensagem">
       <input type="button" onclick="enviar();" value="Enviar">
     </section>
   </body>
 </html>

Espero que essa dica seja tão útil para você como foi para o meu projeto.

Compartilhe este post