Socket.IO認証


123

Node.jsでSocket.IOを使用しようとしています。サーバーが各Socket.IOクライアントにIDを提供できるようにしています。ソケットコードはhttpサーバーコードのスコープ外にあるため、送信されたリクエスト情報に簡単にアクセスできないため、接続中に送信する必要があると想定しています。に最適な方法は何ですか

1)Socket.IOを介して接続しているユーザーに関する情報をサーバーに取得する

2)彼らが誰であるかを認証します(物事が簡単になる場合は、現在Expressを使用しています)

回答:


104

connect-redisを使用し、すべての認証済みユーザーのセッションストアとしてredisを使用します。認証時に、必ずキー(通常は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;
      });
    }

  });
});

3
これに関する新しい興味深いリンクがあります=> danielbaulig.de/socket-ioexpress
Alfred

1
ああ!そのリンクは本当に良いです。これは古くなっています(Socket.IO 0.6.3を使用)。基本的に同じ概念。Cookieを取得し、セッションストアにチェックインして認証します。)
Shripad Krishna

@NightWolfフラッシュ(アクションスクリプト)ではなくJavaScriptでCookieをフェッチしているため、機能するはずです。GetCookieJavaScript関数です。
Shripad Krishna

1
@アルフレッドそのリンクは今死んでいるようです:(
プロQ

@Alfredのリンクが再び有効になります2018-02-01
Tom

32

pusherappプライベートチャンネルを行う方法も気に入りました。ここに画像の説明を入力してください

一意のソケットIDが生成され、Pusherによってブラウザに送信されます。これは、ユーザーが既存の認証システムに対してチャネルにアクセスすることを許可するAJAX要求を介してアプリケーション(1)に送信されます。成功した場合、アプリケーションは、Pusherシークレットで署名されたブラウザに認証文字列を返します。これはWebSocketを介してPusherに送信され、認証文字列が一致した場合に認証が完了します(2)。

なぜならsocket.io、すべてのソケットに対して一意のsocket_id もあるからです。

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

彼らは署名れた認証文字列を使用しましたをユーザーを認証しました。

まだこれをsocket.ioにミラーリングしていませんが、かなり興味深いコンセプトかもしれません。


3
これは素晴らしいです。ただし、アプリサーバーとWebSocketサーバーが分離されていない場合は、Cookieを使用する方が簡単です。ただし、通常は2つを分離する必要があります(分離すると、ソケットサーバーのスケーリングが容易になります)。だからそれは良いです:)
Shripad Krishna

1
@Shripadあなたは完全に真実であり、私はあなたの実装も本当に好きです:P
Alfred

27

私はこれが少し古いことを知っていますが、Cookieを解析してストレージからセッションを取得するアプローチ(例:passport.socketio)に加えて、将来の読者のために)、トークンベースの方法を検討することもできます。

この例では、かなり標準的なJSON Web Tokenを使用しています。クライアントページにトークンを与える必要があります。この例では、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
});

この方法とCookieについての詳細な説明は、こちらに書いてあります


おい!簡単な質問です。クライアントでデコードできないのに、なぜトークンと共にプロファイルを送信するのですか?
Carpetfizz、2015年

できる。JWTはbase64であり、デジタル署名されています。クライアントはそれをデコードできますが、この例では署名を検証できません。
ホセF. Romaniello

3

以下は、次のように機能させるための私の試みです。

  • エクスプレス:4.14
  • socket.io:1.5
  • パスポート(セッションを使用):0.3
  • redis:2.6(セッションを処理するための非常に高速なデータ構造ですが、MongoDBなどの他のものも使用できます。ただし、これをセッションデータ+ MongoDBに使用して、ユーザーなどの他の永続データを格納することをお勧めします)

いくつかのAPIリクエストも追加したい場合があるので、HTTPとWebソケットの両方を同じポートで動作させるためにhttpパッケージも使用します。


server.js

次の抜粋には、以前のテクノロジーをセットアップするために必要なすべてが含まれています。ここで、私のプロジェクトの1つで使用した完全な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}`);
});

参照:

コード内で参照できなかったので、ここに移動しました。

1:パスポート戦略を設定する方法:https : //scotch.io/tutorials/easy-node-authentication-setup-and-local#handling-signupregistration


2

この記事(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));
    });
});

2

セッションを使用し、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();
});

Node.jsエンドポイントの検証に使用しているのと同じコードをプラグインするだけの場合(ただし、リクエストオブジェクトを処理している部分を微調整する必要があります)、ルートにトークンを再利用できます。
Nick Pineda

-5

これでいい

//server side

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

//client side

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

console.log(io.sessionid)

1
私の場合は
io.socket.sessionid

8
これは答えの試みでさえありません。これは認証ではなく、単に接続を確立するだけです。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.