Socket.IO 인증


123

Node.js에서 Socket.IO를 사용하려고하는데 서버가 각 Socket.IO 클라이언트에 ID를 부여하도록 허용하려고합니다. 소켓 코드가 http 서버 코드의 범위를 벗어 났기 때문에 전송 된 요청 정보에 쉽게 액세스 할 수 없으므로 연결 중에 전송되어야한다고 가정합니다. 가장 좋은 방법은 무엇입니까

1) Socket.IO를 통해 연결하는 사람에 대한 정보를 서버에 가져옵니다.

2) 그들이 말하는 사람을 인증하십시오 (현재 Express를 사용하고 있습니다.

답변:


104

connect-redis를 사용하고 인증 된 모든 사용자에 대한 세션 저장소로 redis를 사용합니다. 인증시 클라이언트에 키 (일반적으로 req.sessionID)를 보내야합니다. 클라이언트가이 키를 쿠키에 저장하도록합니다.

소켓 연결시 (또는 나중에 언제든지) 쿠키에서이 키를 가져와 서버로 다시 보냅니다. 이 키를 사용하여 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 사용)! 본질적으로 동일한 개념입니다. 쿠키 가져 오기, 세션 저장소 체크인 및 인증 :)
Shripad Krishna

@NightWolf 플래시 (actionscript)가 아닌 자바 스크립트에서 쿠키를 가져 오기 때문에 작동합니다. GetCookie자바 스크립트 기능입니다.
Shripad Krishna 2012

1
@Alfred 그 링크는 지금 죽은 것 같습니다 :(
Pro 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
굉장합니다. 그러나 앱 서버와 웹 소켓 서버가 분리되어 있지 않으면 쿠키를 사용하는 것이 더 쉬울 것입니다. 그러나 일반적으로 둘을 분리하고 싶습니다 (분리하면 소켓 서버를 확장하는 것이 더 쉬울 것입니다). 그래서 좋습니다 :)
Shripad Krishna 2011

1
@Shripad 완전히 해당 나는 또한 정말 구현 같은 : P
알프레드

27

나는 이것이 조금 오래되었다는 것을 알고 있지만, 미래의 독자를 위해 쿠키를 구문 분석하고 저장소 (예 : passport.socketio ) 에서 세션을 검색하는 방법 외에도 토큰 기반 접근 방식을 고려할 수 있습니다.

이 예에서는 꽤 표준 인 JSON 웹 토큰을 사용합니다. 클라이언트 페이지에 토큰을 제공해야합니다.이 예에서는 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
});

여기 에이 방법과 쿠키에 대한 자세한 설명을 썼습니다 .


야! 빠른 질문입니다. 클라이언트에서 디코딩 할 수없는 경우 토큰과 함께 프로필을 보내는 이유는 무엇입니까?
Carpetfizz 15.01.05

할 수 있습니다. JWT는 디지털 서명 된 base64입니다. 클라이언트는이를 디코딩 할 수 있지만이 예제에서는 서명의 유효성을 검사 할 수 없습니다.
José F. Romaniello 2015-06-09

3

다음은 다음과 같은 작업을 시도합니다.

  • 익스프레스 : 4.14
  • socket.io : 1.5
  • 여권 (세션 사용) : 0.3
  • redis : 2.6 (세션을 처리하는 데 매우 빠른 데이터 구조.하지만 MongoDB와 같은 다른 사용자도 사용할 수 있습니다. 그러나 세션 데이터 + MongoDB에이를 사용하여 Users와 같은 다른 영구 데이터를 저장하는 것이 좋습니다)

일부 API 요청도 추가 할 수 있으므로 http 패키지를 사용 하여 동일한 포트에서 HTTP 및 웹 소켓이 모두 작동하도록 할 것입니다.


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

참조 :

코드 내부를 참조 할 수 없어서 여기로 옮겼습니다.

1 : Passport 전략 설정 방법 : 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/ )에서는 방법을 보여줍니다.

  • Redis에 HTTP 서버의 세션 저장 (Predis 사용)
  • 쿠키에서 보낸 세션 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 2016

-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.