Socket.IO authentification
-
15-10-2019 - |
Question
Je suis en train d'utiliser Socket.IO dans Node.js, et essaie de permettre au serveur de donner une identité à chacun des clients Socket.IO. Comme le code socket est en dehors du champ d'application du code du serveur http, il n'a pas accès facile à l'information demande écrite adressée, donc je suppose qu'il devra être envoyé pendant la connexion. Quelle est la meilleure façon de
1) obtenir les informations au serveur sur qui se connecte via Socket.IO
2) Authentifier qui ils disent qu'ils sont (je suis actuellement en utilisant Express, si cela rend les choses plus facile)
La solution
Utiliser Connect-Redis et ont Redis que votre magasin de session pour tous les utilisateurs authentifiés. Assurez-vous que l'authentification vous envoyez la clé (normalement req.sessionID) au client. Avoir la boutique client cette clé dans un cookie.
Sur la prise de connexion (ou à tout moment plus tard) chercher cette clé à partir du cookie et l'envoyer au serveur. Fetch les informations de session à l'aide de cette clé Redis. (Touche GET)
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});
});
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;
});
}
});
});
Autres conseils
J'ai aussi aimé la façon dont pusherapp fait chaînes privées .
A id prise unique est généré et envoyé au navigateur par Pusher. C'est envoyé à votre application (1) par l'intermédiaire d'un demande AJAX qui autorise l'utilisateur pour accéder au canal contre votre système d'authentification existant. Si retours avec succès de votre application une chaîne d'autorisation au navigateur signé avec vous un secret Pusher. C'est envoyé à Pusher sur le WebSocket, qui vient compléter l'autorisation (2) si les matchs de chaîne d'autorisation.
Parce que aussi socket.io
a socket_id unique pour chaque prise.
socket.on('connect', function() {
console.log(socket.transport.sessionid);
});
Ils ont utilisé chaînes d'autorisation signée pour autoriser les utilisateurs.
Je ne l'ai pas encore à ce miroir socket.io
, mais je pense que ce pourrait être concept assez intéressant.
Je sais que c'est un peu vieux, mais pour les futurs lecteurs, en plus de l'approche de l'analyse syntaxique cookie et la récupération de la session du stockage (par exemple. passport.socketio ) vous pouvez également envisager une approche basée sur des jetons.
Dans cet exemple, j'utilise JSON Tokens Web qui sont assez standard. Il faut donner à la page client le jeton, dans cet exemple imaginer un point final d'authentification que les rendements 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});
});
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 du client vous suffit de le fixer 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 .
Cet article ( http: //simplapi.wordpress.com/2012/04/13/php-and-node-js-session-share-redi/ ) montre comment
- sessions de magasin du serveur HTTP dans Redis (en utilisant Predis)
- obtenir ces sessions de Redis à Node.js par l'identifiant de session envoyé dans un cookie
En utilisant ce code, vous pouvez les obtenir dans socket.io aussi.
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 le travail suivant:
- express : 4,14
- socket.io : 1.5
- passeport (en utilisant des sessions): 0,3
- Redis : 2.6 (structure de données vraiment rapide à des séances de poignée, mais vous pouvez utiliser d'autres comme MongoDB aussi Cependant, je vous encourage à utiliser les données de session + MongoDB pour stocker d'autres données persistantes comme. utilisateurs)
Puisque vous pouvez ajouter des requêtes API ainsi, nous allons également utiliser http package pour avoir HTTP et socket Web travaillant dans le même port.
server.js
L'extrait suivant ne comprend que tout ce que vous devez définir les technologies précédentes vers le haut. Vous pouvez voir la version complète de server.js que j'utilisé dans l'un de mes projets .
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}`);
prises / index.js
Notre socketConnectionHandler
, je ne aime pas mettre tout à l'intérieur server.js (même si vous parfaitement possible), d'autant plus que ce fichier peut finir par contenir tout à fait 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 le client du JavaScript 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}`);
});
Références:
Je ne pouvait pas faire référence à l'intérieur du code, donc je me suis déplacé ici.
1: Comment mettre en place vos stratégies Passeport: https://scotch.io/tutorials/easy-node-authentication-setup-and-local#handling-signupregistration
session d'utilisation et Redis entre c / s
// 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)