Socket.IO 身份验证

发布于 2024-10-13 06:32:56 字数 224 浏览 4 评论 0原文

我正在尝试在 Node.js 中使用 Socket.IO,并尝试允许服务器为每个 Socket.IO 客户端提供身份。由于套接字代码超出了 http 服务器代码的范围,因此它无法轻松访问发送的请求信息,因此我假设需要在连接期间发送它。 最好的方法是什么?

1)向服务器获取有关谁通过 Socket.IO 连接的信息

2)验证他们所说的身份(我目前正在使用 Express,如果这会让事情变得更容易),

I am trying to use Socket.IO in Node.js, and am trying to allow the server to give an identity to each of the Socket.IO clients. As the socket code is outside the scope of the http server code, it doesn't have easy access to the request information sent, so I'm assuming it will need to be sent up during the connection. What is the best way to

1) get the information to the server about who is connecting via Socket.IO

2) authenticate who they say they are (I'm currently using Express, if that makes things any easier)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

离线来电— 2024-10-20 06:32:56

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

在套接字连接时(或以后的任何时候)从 cookie 中获取此密钥并将其发送回服务器。使用这个key获取redis中的session信息。 (GET key)

例如:

服务器端(使用 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;
      });
    }

  });
});

Use connect-redis and have redis as your session store for all authenticated users. Make sure on authentication you send the key (normally req.sessionID) to the client. Have the client store this key in a cookie.

On socket connect (or anytime later) fetch this key from the cookie and send it back to the server. Fetch the session information in redis using this key. (GET key)

Eg:

Server side (with redis as session store):

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

Client side:

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

Server side:

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

  });
});
丶视觉 2024-10-20 06:32:56

我也喜欢 pusherapp 的方式 私人频道在此处输入图像描述

生成一个唯一的套接字 ID,并且
由 Pusher 发送到浏览器。这是
通过以下方式发送至您的申请 (1)
授权用户的 AJAX 请求
访问针对您的频道
现有的认证系统。如果
成功您的申请会返回
浏览器的授权字符串
与你签署的 Pusher 秘密。这是
通过 WebSocket 发送到 Pusher,
至此完成授权(2)
如果授权字符串匹配。

因为每个套接字都有唯一的 socket_id。

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

他们使用签名授权字符串来授权用户。

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

I also liked the way pusherapp does private channels.enter image description here

A unique socket id is generated and
sent to the browser by Pusher. This is
sent to your application (1) via an
AJAX request which authorizes the user
to access the channel against your
existing authentication system. If
successful your application returns an
authorization string to the browser
signed with you Pusher secret. This is
sent to Pusher over the WebSocket,
which completes the authorization (2)
if the authorization string matches.

Because also socket.io has unique socket_id for every socket.

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

They used signed authorization strings to authorize users.

I haven't yet mirrored this to socket.io, but I think it could be pretty interesting concept.

樱桃奶球 2024-10-20 06:32:56

我知道这有点老了,但对于未来的读者来说,除了解析 cookie 和从存储中检索会话的方法之外(例如 passport.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: '[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});
});

现在,您的 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 的更详细说明 此处

I know this is bit old, but for future readers in addition to the approach of parsing cookie and retrieving the session from the storage (eg. passport.socketio ) you might also consider a token based approach.

In this example I use JSON Web Tokens which are pretty standard. You have to give to the client page the token, in this example imagine an authentication endpoint that returns 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});
});

Now, your socket.io server can be configured as follows:

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

The socket.io-jwt middleware expects the token in a query string, so from the client you only have to attach it when connecting:

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

I wrote a more detailed explanation about this method and cookies here.

终难愈 2024-10-20 06:32:56

这是我尝试进行以下工作:

  • express:4.14
  • socket.io:1.5
  • passport(使用会话):0.3
  • redis :2.6(处理会话的数据结构非常快;但是您也可以使用其他数据结构,例如 MongoDB。但是,我鼓励您将其用于会话数据 + MongoDB 来存储其他持久数据,例如用户),

因为您可能想要添加一些 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 (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(`

Here is my attempt to have the following working:

  • express: 4.14
  • socket.io: 1.5
  • passport (using sessions): 0.3
  • redis: 2.6 (Really fast data structure to handle sessions; but you can use others like MongoDB too. However, I encourage you to use this for session data + MongoDB to store other persistent data like Users)

Since you might want to add some API requests as well, we'll also use http package to have both HTTP and Web socket working in the same port.


server.js

The following extract only includes everything you need to set the previous technologies up. You can see the complete server.js version which I used in one of my projects here.

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

Our socketConnectionHandler, I just don't like putting everything inside server.js (even though you perfectly could), especially since this file can end up containing quite a lot of code pretty quickly.

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

Extra material (client):

Just a very basic version of what the JavaScript socket.io client could be:

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

References:

I just couldn't reference inside the code, so I moved it here.

1: How to set up your Passport strategies: https://scotch.io/tutorials/easy-node-authentication-setup-and-local#handling-signupregistration

计㈡愣 2024-10-20 06:32:56

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

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

使用此代码您也可以在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));
    });
});

This article (http://simplapi.wordpress.com/2012/04/13/php-and-node-js-session-share-redi/) shows how to

  • store sessions of the HTTP server in Redis (using Predis)
  • get these sessions from Redis in node.js by the session id sent in a cookie

Using this code you are able to get them in socket.io, too.

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));
    });
});
怀里藏娇 2024-10-20 06:32:56

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

use session and Redis between c/s

Server side

io.use(function(socket, next) {
    // get here session id 
    console.log(socket.handshake.headers.cookie); and match from redis session data
    next();
});
慵挽 2024-10-20 06:32:56

这应该可以做到

//server side

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

//client side

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

console.log(io.sessionid)

this should do it

//server side

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

//client side

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

console.log(io.sessionid)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文