Socket.IO 身份验证

我尝试在 Node.js 中使用 Socket.IO,并尝试允许服务器为每个 Socket.IO 客户机提供一个标识。由于套接字代码超出了 http 服务器代码的范围,因此不能很容易地访问发送的请求信息,所以我假设在连接期间需要发送该请求信息。什么是最好的方法

1)将通过 Socket.IO 连接的用户信息传递给服务器

2)验证他们自称的身份(我目前使用 Express,如果这能让事情变得更简单的话)

82692 次浏览

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

在套接字连接上(或以后任何时候)从 cookie 中获取这个密钥并将其发送回服务器。使用此键获取重写中的会话信息。(拿钥匙)

例如:

服务器端(以 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私人频道的方式。enter image description here

生成一个惟一的套接字 id,然后 由 Pusher 发送到浏览器,这是 (1)透过互联网递交申请 授权用户的 AJAX 请求 进入你的频道 现有的认证系统 成功的应用程序返回一个 授权字符串添加到浏览器 和你签署了推动者的秘密。这是 通过 WebSocket 发送给 Pusher, 完成授权(2) 如果授权字符串匹配。

因为 socket.io对每个套接字都有唯一的 socket _ id。

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

他们使用 经过签名的授权字符串授权用户。

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

这个应该够了

//server side


io.sockets.on('connection', function (con) {
console.log(con.id)
})


//client side


var io = io.connect('http://...')


console.log(io.sessionid)

本文(http://simplapi.wordpress.com/2012/04/13/php-and-node-js-session-share-redi/)展示了如何

  • 将 HTTP 服务器的会话存储在 Redis 中(使用 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));
});
});

我知道这有点老旧,但是对于未来的读者来说,除了解析 cookie 和从存储中检索会话的方法之外(例如,。护照 Socketio)你也可以考虑一种基于令牌的方法。

在这个例子中,我使用了相当标准的 JSON Web 令牌。您必须向客户端页面提供令牌,在这个示例中,设想一个返回 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});
});

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

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

Io-jwt 中间件需要查询字符串中的令牌,因此只需在连接时从客户机附加该令牌:

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

我写了一个关于这个方法和 Cookie 给你的更详细的解释。

在信用证之间使用会话和 Redis

服务器端

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

以下是我的尝试:

  • Express : 4.14
  • Socket.io : 1.5
  • 护照 (使用会话) : 0.3
  • Redis : 2.6(处理会话的数据结构非常快; 但是您也可以使用 MongoDB 等其他数据结构。但是,我鼓励您使用会话数据 + MongoDB 来存储其他持久性数据(如用户)

由于您可能还需要添加一些 API 请求,因此我们还将使用 译注:包使 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 (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

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

参考文献:

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

如何设置您的护照策略: https://scotch.io/tutorials/easy-node-authentication-setup-and-local#handling-signupregistration