web-dev-qa-db-fra.com

Authentification Socket.IO

J'essaie d'utiliser Socket.IO dans Node.js et j'autorise le serveur à donner une identité à chacun des clients Socket.IO. Le code de socket n'entrant pas dans le champ du code du serveur http, il n'a pas facilement accès aux informations de requête 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 prétendent être (j'utilise actuellement Express, si cela facilite les choses)

114
Ryan

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

Sur socket, connectez-vous (ou à tout moment plus tard) extrayez cette clé du cookie et renvoyez-la au serveur. Récupérez les informations de session dans redis en utilisant 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;
      });
    }

  });
});
102
Shripad Krishna

J'ai aussi aimé le chemin pusherapp fait canaux privés .enter image description here

Un identifiant de socket unique est généré et envoyé au navigateur par Pusher. Celui-ci 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 que vous avez signé. Pusher secret: il est envoyé à Pusher par le WebSocket, qui complète l'autorisation (2) si la chaîne d'autorisation correspond.

Parce que aussi socket.io possède un socket_id unique pour chaque socket.

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

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

Je ne l'ai pas encore reflété à socket.io, mais je pense que ce pourrait être un concept assez intéressant.

32
Alfred

Je sais que cela est un peu vieux, mais pour les futurs lecteurs, en plus de l’analyse du cookie et de la récupération de 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 des jetons Web JSON qui sont assez standard. Vous devez donner le jeton à la page client. Dans cet exemple, imaginez un noeud final 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. Il suffit donc au client 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 .

24
José F. Romaniello

Voici ma tentative d'avoir les travaux suivants:

  • express: 4.14
  • socket.io: 1.5
  • passeport ​​(en utilisant des sessions): 0.3
  • redis: 2.6 (Structure de données très rapide pour gérer les sessions; mais vous pouvez également utiliser d'autres sources telles que MongoDB. Cependant, je vous encourage à l'utiliser pour les données de session + MongoDB pour stocker d'autres données persistantes telles que Utilisateurs)

Puisque vous voudrez peut-être également ajouter des requêtes d'API, nous utiliserons également le paquet http pour que HTTP et le socket Web fonctionnent sur le même port.


server.js

L'extrait suivant ne comprend que tout ce dont vous avez besoin pour configurer 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 (i.e. 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 pas tout mettre dans server.js (même si vous le pouvez parfaitement), d'autant plus que ce fichier peut 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 basique de ce que pourrait être le client 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}`);
});

Les références:

Je ne pouvais pas faire référence à l'intérieur du code, je l'ai donc déplacé ici.

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

2
zurfyx

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

  • sessions de stockage du serveur HTTP dans Redis (à l'aide de Predis)
  • récupérez ces sessions de Redis dans node.js à l'aide de l'ID 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));
    });
});
2
Blade1336

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();
});
1
onplanner