我正在尝试在 Node.js 中使用 Socket.IO,并试图允许服务器为每个 Socket.IO 客户端提供身份。由于套接字代码超出了 http 服务器代码的范围,因此无法轻松访问发送的请求信息,因此我假设它需要在连接期间发送。最好的方法是什么
1) 向服务器获取有关谁通过 Socket.IO 连接的信息
2)验证他们说的是谁(我目前正在使用 Express,如果这让事情变得更容易的话)
我正在尝试在 Node.js 中使用 Socket.IO,并试图允许服务器为每个 Socket.IO 客户端提供身份。由于套接字代码超出了 http 服务器代码的范围,因此无法轻松访问发送的请求信息,因此我假设它需要在连接期间发送。最好的方法是什么
1) 向服务器获取有关谁通过 Socket.IO 连接的信息
2)验证他们说的是谁(我目前正在使用 Express,如果这让事情变得更容易的话)
使用 connect-redis 并将 redis 作为所有经过身份验证的用户的会话存储。确保在身份验证时将密钥(通常是 req.sessionID)发送给客户端。让客户端将此密钥存储在 cookie 中。
在套接字连接上(或以后的任何时间)从 cookie 中获取此密钥并将其发送回服务器。使用此键获取 redis 中的会话信息。(获取密钥)
例如:
服务器端(使用 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;
});
}
});
});
Pusher 会生成一个唯一的套接字 id 并将其发送到浏览器。这通过 AJAX 请求发送到您的应用程序 (1),该请求授权用户根据您现有的身份验证系统访问通道。如果成功,您的应用程序将向使用您的 Pusher 密码签名的浏览器返回一个授权字符串。这通过 WebSocket 发送到 Pusher,如果授权字符串匹配,则完成授权 (2)。
因为socket.io
每个套接字也有唯一的 socket_id。
socket.on('connect', function() {
console.log(socket.transport.sessionid);
});
他们使用签名的授权字符串来授权用户。
我还没有将此反映到socket.io
,但我认为这可能是一个非常有趣的概念。
我知道这有点老了,但是对于未来的读者来说,除了解析 cookie 和从存储中检索会话的方法(例如passport.socketio)之外,您还可以考虑基于令牌的方法。
在这个例子中,我使用了非常标准的 JSON Web Tokens。您必须向客户端页面提供令牌,在此示例中,想象一个返回 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');
});
socket.io-jwt 中间件需要查询字符串中的令牌,因此从客户端您只需在连接时附加它:
var socket = io.connect('', {
query: 'token=' + token
});
我在这里写了关于这个方法和cookies的更详细的解释。
这是我尝试进行以下工作的尝试:
由于您可能还想添加一些 API 请求,我们还将使用http包让 HTTP 和 Web 套接字在同一个端口上工作。
以下摘录仅包含设置先前技术所需的一切。您可以在此处查看我在其中一个项目中使用的完整 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}`);
我们的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
这篇文章(http://simplapi.wordpress.com/2012/04/13/php-and-node-js-session-share-redi/)展示了如何
使用此代码,您也可以在 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));
});
});
c/s 之间使用 session 和 Redis
服务器端
io.use(function(socket, next) {
// get here session id
console.log(socket.handshake.headers.cookie); 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)