Socket.IO身份validation

我试图在Node.js中使用Socket.IO,并试图让服务器给每个Socket.IO客户端一个身份。 由于套接字代码超出了http服务器代码的范围,因此它不容易访问发送的请求信息,所以我假定在连接期间需要发送它。 什么是最好的方式

1)通过Socket.IO获取有关谁正在连接的信息

2)validation他们说他们是谁(我目前正在使用Express,如果这使事情更容易)

使用connect-redis并将redis作为所有经过身份validation的用户的会话存储。 确保在发送密钥时(通常是req.sessionID)到客户端。 让客户端将这个密钥存储在一个cookie中。

在套接字连接(或随后的任何时候)从cookie获取此密钥,并将其发送回服务器。 使用此密钥在redis中获取会话信息。 (GET键)

例如:

服务器端(使用redis作为会话存储):

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

客户端:

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

服务器端:

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

我也喜欢pusherapp做私人频道的方式。 在这里输入图像说明

生成一个唯一的套接字ID,并通过Pusher发送给浏览器。 这通过AJAX请求发送到您的应用程序(1),该请求授权用户访问您的现有authentication系统的频道。 如果您的应用程序成功返回一个授权string到您与Pusher秘密签名的浏览器。 这通过WebSocket发送给Pusher,如果授权string匹配,则完成授权(2)。

因为socket.io也为每个套接字都有唯一的socket_id。

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

他们使用签名授权string来授权用户。

我还没有把这个映射到socket.io ,但我认为这可能是一个非常有趣的概念。

我知道这有点旧了,但是对于将来的读者,除了parsingcookie和从存储中检索会话(例如passport.socketio )的方法之外,您还可以考虑基于令牌的方法。

在这个例子中,我使用了非常标准的JSON Web Tokens。 您必须向客户端页面提供令牌,在本例中,设想一个返回JWT的authentication端点:

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

现在,您的socket.io服务器可以configuration如下:

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

socket.io-jwt中间件期望查询string中的令牌,所以从客户端连接时只需要附加它:

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

我在这里写了一个关于这个方法和cookie的更详细的解释。

这篇文章( http://simplapi.wordpress.com/2012/04/13/php-and-node-js-session-share-redi/ )显示如何

  • 在Redis中存储HTTP服务器的会话(使用Predis)
  • 通过cookie中发送的会话ID从node.js中获取来自Redis的这些会话

使用这个代码你也可以在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)); }); }); 

这是我的尝试,有以下工作:

  • expression :4.14
  • socket.io:1.5
  • 护照 (使用会议):0.3
  • redis :2.6(真正快速的数据结构来处理会话;但是你也可以使用MongoDB等其他的数据结构,但是我鼓励你使用这个会话数据+ MongoDB来存储其他持久数据,比如Users)

既然你可能也想添加一些API请求,我们也将使用http包使HTTP和Web套接字在同一个端口上工作。


server.js

以下摘录仅包含您设置以前技术所需的一切。 你可以在我的项目中看到完整的server.js版本。

 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

我们的socketConnectionHandler ,我只是不喜欢把一切都放在server.js(即使你完全可以),特别是因为这个文件可以很快包含了很多代码。

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

额外材料(客户):

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

参考文献:

我只是不能在代码中引用,所以我把它移到了这里。

1:如何设置Passport策略: https : //scotch.io/tutorials/easy-node-authentication-setup-and-local#handling-signupregistration

在c / s之间使用会话和redis

// 服务器端

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

这应该做到这一点

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