Autenticación de Socket.IO

Estoy tratando de usar Socket.IO en Node.js, y estoy tratando de permitir que el servidor dé una identidad a cada uno de los clientes de Socket.IO. Como el código de socket está fuera del scope del código del servidor http, no tiene fácil acceso a la información de solicitud enviada, por lo que supongo que tendrá que enviarse durante la conexión. ¿Cuál es la mejor manera de

1) obtener la información del servidor sobre quién se conecta a través de Socket.IO

2) autenticar quiénes dicen que son (actualmente estoy usando Express, si eso hace las cosas más fáciles)

Utilice connect-redis y tenga redis como su tienda de sesiones para todos los usuarios autenticados. Asegúrese de que en la autenticación envíe la clave (normalmente req.sessionID) al cliente. Haga que el cliente almacene esta clave en una cookie.

En la conexión de socket (o en cualquier momento posterior), obtenga esta clave de la cookie y envíela al servidor. Obtenga la información de la sesión en redis usando esta clave. (Obtener la clave)

P.ej:

Lado del servidor (con redis como almacén de sesiones):

req.session.regenerate... res.send({rediskey: req.sessionID}); 

Lado del cliente:

 //store the key in a cookie SetCookie('rediskey', <%= rediskey %>); //http://msdn.microsoft.com/en-us/library/ms533693(v=vs.85).aspx //then when socket is connected, fetch the rediskey from the document.cookie and send it back to server var socket = new io.Socket(); socket.on('connect', function() { var rediskey = GetCookie('rediskey'); //http://msdn.microsoft.com/en-us/library/ms533693(v=vs.85).aspx socket.send({rediskey: rediskey}); }); 

Lado del servidor:

 //in io.on('connection') io.on('connection', function(client) { client.on('message', function(message) { if(message.rediskey) { //fetch session info from redis redisclient.get(message.rediskey, function(e, c) { client.user_logged_in = c.username; }); } }); }); 

También me gustó la forma en que pusherapp hace canales privados . enter image description here

Un identificador de socket único es generado y enviado al navegador por Pusher. Esto se envía a su aplicación (1) a través de una solicitud AJAX que autoriza al usuario a acceder al canal contra su sistema de autenticación existente. Si tiene éxito, su aplicación devuelve una cadena de autorización al navegador firmado con su secreto Pusher. Esto se envía a Pusher a través de WebSocket, que completa la autorización (2) si la cadena de autorización coincide.

Porque también socket.io tiene socket.io único para cada socket.

 socket.on('connect', function() { console.log(socket.transport.sessionid); }); 

Utilizaron cadenas de autorización firmadas para autorizar a los usuarios.

Todavía no he reflejado esto en socket.io , pero creo que podría ser un concepto bastante interesante.

Sé que esto es un poco viejo, pero para futuros lectores, además del enfoque de analizar cookies y recuperar la sesión del almacenamiento (por ejemplo, pasaporte.socketio ), también podrías considerar un enfoque basado en tokens.

En este ejemplo, uso JSON Web Tokens, que son bastante estándar. Tiene que dar a la página del cliente el token, en este ejemplo imagine un punto final de autenticación que devuelve JWT:

 var jwt = require('jsonwebtoken'); // other requires app.post('/login', function (req, res) { // TODO: validate the actual user user var profile = { first_name: 'John', last_name: 'Doe', email: 'john@doe.com', id: 123 }; // we are sending the profile in the token var token = jwt.sign(profile, jwtSecret, { expiresInMinutes: 60*5 }); res.json({token: token}); }); 

Ahora, su servidor socket.io se puede configurar de la siguiente manera:

 var socketioJwt = require('socketio-jwt'); var sio = socketIo.listen(server); sio.set('authorization', socketioJwt.authorize({ secret: jwtSecret, handshake: true })); sio.sockets .on('connection', function (socket) { console.log(socket.handshake.decoded_token.email, 'has joined'); //socket.on('event'); }); 

El middleware socket.io-jwt espera el token en una cadena de consulta, por lo que desde el cliente solo tiene que adjuntarlo al conectarse:

 var socket = io.connect('', { query: 'token=' + token }); 

Escribí una explicación más detallada sobre este método y las cookies aquí .

Este artículo ( http://simplapi.wordpress.com/2012/04/13/php-and-node-js-session-share-redi/ ) muestra cómo

  • almacenar sesiones del servidor HTTP en Redis (usando Predis)
  • obtener estas sesiones de Redis en node.js mediante el ID de sesión enviado en una cookie

Usando este código también puedes obtenerlos en socket.io.

 var io = require('socket.io').listen(8081); var cookie = require('cookie'); var redis = require('redis'), client = redis.createClient(); io.sockets.on('connection', function (socket) { var cookies = cookie.parse(socket.handshake.headers['cookie']); console.log(cookies.PHPSESSID); client.get('sessions/' + cookies.PHPSESSID, function(err, reply) { console.log(JSON.parse(reply)); }); }); 

Aquí está mi bash de tener el siguiente trabajo:

  • express : 4.14
  • socket.io : 1.5
  • pasaporte (usando sesiones): 0.3
  • redis : 2.6 (Estructura de datos muy rápida para manejar sesiones, pero puedes usar otras como MongoDB también. Sin embargo, te animo a usar esto para datos de sesión + MongoDB para almacenar otros datos persistentes como Usuarios)

Dado que es posible que desee agregar algunas solicitudes de la API también, también utilizaremos el paquete http para que tanto el socket HTTP como el Web funcionen en el mismo puerto.


server.js

El siguiente extracto solo incluye todo lo que necesita para configurar las tecnologías anteriores. Puede ver la versión completa de server.js que utilicé en uno de mis proyectos aquí .

 import http from 'http'; import express from 'express'; import passport from 'passport'; import { createClient as createRedisClient } from 'redis'; import connectRedis from 'connect-redis'; import Socketio from 'socket.io'; // Your own socket handler file, it's optional. Explained below. import socketConnectionHandler from './sockets'; // Configuration about your Redis session data structure. const redisClient = createRedisClient(); const RedisStore = connectRedis(Session); const dbSession = new RedisStore({ client: redisClient, host: 'localhost', port: 27017, prefix: 'stackoverflow_', disableTTL: true }); // Let's configure Express to use our Redis storage to handle // sessions as well. You'll probably want Express to handle your // sessions as well and share the same storage as your socket.io // does (ie for handling AJAX logins). const session = Session({ resave: true, saveUninitialized: true, key: 'SID', // this will be used for the session cookie identifier secret: 'secret key', store: dbSession }); app.use(session); // Let's initialize passport by using their middlewares, which do //everything pretty much automatically. (you have to configure login // / register strategies on your own though (see reference 1) app.use(passport.initialize()); app.use(passport.session()); // Socket.IO const io = Socketio(server); io.use((socket, next) => { session(socket.handshake, {}, next); }); io.on('connection', socketConnectionHandler); // socket.io is ready; remember that ^this^ variable is just the // name that we gave to our own socket.io handler file (explained // just after this). // Start server. This will start both socket.io and our optional // AJAX API in the given port. const port = 3000; // Move this onto an environment variable, // it'll look more professional. server.listen(port); console.info(`🌐 API listening on port ${port}`); console.info(`🗲 Socket listening on port ${port}`); 

sockets / index.js

Nuestro socketConnectionHandler , simplemente no me gusta poner todo dentro de server.js (aunque perfectamente podrías), especialmente porque este archivo puede terminar conteniendo bastante código bastante rápido.

 export default function connectionHandler(socket) { const userId = socket.handshake.session.passport && socket.handshake.session.passport.user; // If the user is not logged in, you might find ^this^ // socket.handshake.session.passport variable undefined. // Give the user a warm welcome. console.info(`⚡︎ New connection: ${userId}`); socket.emit('Grettings', `Grettings ${userId}`); // Handle disconnection. socket.on('disconnect', () => { if (process.env.NODE_ENV !== 'production') { console.info(`⚡︎ Disconnection: ${userId}`); } }); } 

Material adicional (cliente):

Una versión muy básica de lo que podría ser el cliente JavaScript socket.io:

 import io from 'socket.io-client'; const socketPath = '/socket.io'; // <- Default path. // But you could configure your server // to something like /api/socket.io const socket = io.connect('localhost:3000', { path: socketPath }); socket.on('connect', () => { console.info('Connected'); socket.on('Grettings', (data) => { console.info(`Server gretting: ${data}`); }); }); socket.on('connect_error', (error) => { console.error(`Connection error: ${error}`); }); 

Referencias

Simplemente no pude hacer referencia dentro del código, así que lo moví aquí.

1: cómo configurar sus estrategias de pasaporte: https://scotch.io/tutorials/easy-node-authentication-setup-and-local#handling-signupregistration

use sesión y redis entre c / s

// lado del servidor

 io.use(function(socket, next) { console.log(socket.handshake.headers.cookie); // get here session id and match from redis session data next(); }); 

esto debería hacerlo

 //server side io.sockets.on('connection', function (con) { console.log(con.id) }) //client side var io = io.connect('http://...') console.log(io.sessionid)