Authentification Socket.IO

J’essaie d’utiliser Socket.IO dans Node.js et j’essaie d’autoriser le serveur à donner une identité à chacun des clients Socket.IO. Comme le code de socket est en dehors de la scope du code du serveur http, il n’a pas d’access facile aux informations de demande envoyées. Je suppose donc qu’il devra être envoyé lors de la connexion. Quelle est la meilleure façon de

1) obtenir les informations sur le serveur pour savoir qui se connecte via Socket.IO

2) authentifier qui ils disent qu’ils sont (j’utilise actuellement Express, si cela facilite les choses)

Utilisez connect-redis et redis comme magasin de session pour tous les utilisateurs authentifiés. Assurez-vous, lors de l’authentification, d’envoyer la clé (normalement req.sessionID) au client. Demandez au client de stocker cette clé dans un cookie.

Sur socket connect (ou à tout moment plus tard), récupérez cette clé à partir du cookie et renvoyez-la au serveur. Récupère les informations de session dans redis à l’aide de cette clé. (Obtenir la clé)

Par exemple:

Côté serveur (avec redis comme magasin de session):

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

Côté client:

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

Du côté serveur:

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

J’ai aussi aimé la façon dont Pusherapp fait des chaînes privées . entrer la description de l'image ici

Un identifiant de socket unique est généré et envoyé au navigateur par Pusher. Ceci est envoyé à votre application (1) via une requête AJAX qui autorise l’utilisateur à accéder au canal par rapport à votre système d’authentification existant. En cas de succès, votre application renvoie une chaîne d’autorisation au navigateur signé avec votre secret Pusher. Ceci est envoyé à Pusher sur WebSocket, qui complète l’autorisation (2) si la chaîne d’autorisation correspond.

Parce que socket.io a aussi socket.io unique socket_id pour chaque socket.

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

Ils ont utilisé des chaînes d’autorisation signées pour autoriser les utilisateurs.

Je ne l’ai pas encore reflété à socket.io , mais je pense que cela pourrait être un concept très intéressant.

Je sais que c’est un peu vieux, mais pour les futurs lecteurs, en plus de l’approche consistant à parsingr les cookies et à récupérer la session depuis le stockage (par exemple, passport.socketio ), vous pouvez également envisager une approche basée sur les jetons.

Dans cet exemple, j’utilise les jetons Web JSON qui sont plutôt standard. Vous devez donner à la page client le jeton, dans cet exemple, imaginez un sharepoint terminaison d’authentification qui renvoie 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: '[email protected]', id: 123 }; // we are sending the profile in the token var token = jwt.sign(profile, jwtSecret, { expiresInMinutes: 60*5 }); res.json({token: token}); }); 

Maintenant, votre serveur socket.io peut être configuré comme suit:

 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'); }); 

Le middleware socket.io-jwt attend le jeton dans une chaîne de requête, donc depuis le client il suffit de l’attacher lors de la connexion:

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

J’ai écrit une explication plus détaillée sur cette méthode et les cookies ici .

Cet article ( http://simplapi.wordpress.com/2012/04/13/php-and-node-js-session-share-redi/ ) montre comment

  • Stocker les sessions du serveur HTTP dans Redis (en utilisant Predis)
  • obtenir ces sessions de Redis dans node.js par l’identifiant de session envoyé dans un cookie

En utilisant ce code, vous pouvez également les obtenir dans 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)); }); }); 

Voici ma tentative d’avoir les travaux suivants:

  • exprimer : 4.14
  • socket.io : 1.5
  • passeport (utilisation des sessions): 0.3
  • redis : 2.6 (Structure de données très rapide pour gérer les sessions, mais vous pouvez également en utiliser d’autres comme MongoDB. Cependant, je vous encourage à utiliser ceci pour les données de session + MongoDB pour stocker d’autres données persistantes comme les utilisateurs)

Étant donné que vous souhaiterez peut-être append des requêtes API, nous utiliserons également le package http pour que le socket HTTP et Web fonctionnent sur le même port.


server.js

L’extrait suivant comprend uniquement tout ce dont vous avez besoin pour définir les technologies précédentes. Vous pouvez voir la version complète de server.js que j’ai utilisée dans l’un de mes projets ici .

 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

Notre socketConnectionHandler , je n’aime tout simplement pas tout mettre dans server.js (même si vous le pouviez parfaitement), d’autant plus que ce fichier peut finir par contenir beaucoup de code assez rapidement.

 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}`); } }); } 

Matériel supplémentaire (client):

Juste une version très simple de ce que le client JavaScript socket.io pourrait être:

 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}`); }); 

Les références:

Je ne pouvais tout simplement pas faire référence au code, alors je l’ai déplacé ici.

1: Comment configurer vos stratégies de passeport: https://scotch.io/tutorials/easy-node-authentication-setup-and-local#handling-signupregistration

utiliser session et redis entre c / s

// du côté serveur

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

cela devrait le faire

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