Socket.IO Kimlik Doğrulaması


123

Node.js'de Socket.IO'yu kullanmaya çalışıyorum ve sunucunun her Socket.IO istemcisine bir kimlik vermesine izin vermeye çalışıyorum. Soket kodu http sunucu kodunun kapsamı dışında olduğundan, gönderilen istek bilgilerine kolay erişime sahip değildir, bu nedenle bağlantı sırasında gönderilmesi gerekeceğini varsayıyorum. En iyi yol nedir

1) Socket.IO aracılığıyla kimin bağlandığı hakkında sunucuya bilgi alın

2) kim olduklarını söylediklerini doğrulayın (işleri kolaylaştıracaksa şu anda Express kullanıyorum)

Yanıtlar:


104

Connect-redis'i kullanın ve kimliği doğrulanmış tüm kullanıcılar için oturum deponuz olarak redis'e sahip olun. Kimlik doğrulamasında anahtarı (normalde req.sessionID) istemciye gönderdiğinizden emin olun. Müşterinin bu anahtarı bir çerezde saklamasını sağlayın.

Soket bağlantısında (veya daha sonra herhangi bir zamanda) bu anahtarı çerezden alır ve sunucuya geri gönderir. Bu anahtarı kullanarak oturum bilgilerini redis olarak alın. (Anahtarı al)

Örneğin:

Sunucu tarafı (oturum deposu olarak redis ile):

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

İstemci tarafı:

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

Sunucu tarafı:

//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
Bununla ilgili yeni bir ilginç bağlantı var => danielbaulig.de/socket-ioexpress
Alfred

1
Aha! Bu bağlantı gerçekten çok iyi. Bu modası geçmiş (Socket.IO 0.6.3 kullanıyor)! Esasen aynı kavram. Çerezi
getir

@NightWolf, çerezi flash'ta değil javascript'te (actionscript) getirdiğiniz için çalışmalıdır. GetCookiejavascript işlevidir.
Shripad Krishna

1
@Alfred şu bağlantı artık yok gibi görünüyor :(
Pro Q

@ Alfred'in bağlantısı tekrar geçerli 2018-02-01
Tom

32

Ben de yolu sevdim pusherapp yapar özel kanallar .görüntü açıklamasını buraya girin

Benzersiz bir soket kimliği oluşturulur ve Pusher tarafından tarayıcıya gönderilir. Bu, uygulamanıza (1), kullanıcıyı mevcut kimlik doğrulama sisteminize karşı kanala erişme yetkisi veren bir AJAX isteği aracılığıyla gönderilir. Başarılı olursa, uygulamanız İtici sırrı ile imzalanan tarayıcıya bir yetkilendirme dizesi döndürür. Bu, yetkilendirme dizesi eşleşirse yetkilendirmeyi (2) tamamlayan WebSocket üzerinden Pusher'a gönderilir.

Çünkü socket.ioher soket için benzersiz bir socket_id var.

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

Kullanıcıları yetkilendirmek için imzalı yetkilendirme dizeleri kullandılar .

Bunu henüz yansıtmadım socket.io, ancak bunun oldukça ilginç bir konsept olabileceğini düşünüyorum.


3
Bu harika. Ancak, uygulama sunucunuz ve websocket sunucunuz ayrı değilse, yalnızca çerezleri kullanmak daha kolay olacaktır. Ancak genellikle ikisini ayırmak istersiniz (ayrılırsa soket sunucusunu ölçeklendirmek daha kolay olacaktır). Yani güzel :)
Shripad Krishna

1
@Shripad tamamen doğrusunuz ve uygulamanızı da gerçekten seviyorum: P
Alfred

27

Bunun biraz eski olduğunu biliyorum, ancak gelecekteki okuyucular için çerezi ayrıştırma ve depodan oturumu geri alma yaklaşımına ek olarak (örn. Passport.socketio ) belirteç tabanlı bir yaklaşımı da düşünebilirsiniz.

Bu örnekte oldukça standart olan JSON Web Token kullanıyorum. İstemci sayfasına belirteci vermelisiniz, bu örnekte JWT döndüren bir kimlik doğrulama uç noktası hayal edin:

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

Artık socket.io sunucunuz aşağıdaki gibi yapılandırılabilir:

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 ara yazılımı belirteci bir sorgu dizesinde bekler, bu nedenle istemciden yalnızca bağlanırken eklemeniz gerekir:

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

Bu yöntem ve çerezler hakkında daha ayrıntılı bir açıklama yazdı buraya .


Hey! Hızlı soru, istemcide kodu çözülemiyorsa neden jetonla profili gönderiyorsunuz?
Carpetfizz

Bu olabilir. JWT sadece base64, dijital imzalı. İstemci bunu çözebilir, ancak bu örnekteki imzayı doğrulayamaz.
José F. Romaniello

3

İşte aşağıdakileri çalıştırma girişimim:

  • ekspres : 4.14
  • socket.io : 1.5
  • pasaport (oturumları kullanarak): 0.3
  • redis : 2.6 (Oturumları işlemek için gerçekten hızlı veri yapısı; ancak MongoDB gibi diğerlerini de kullanabilirsiniz. Ancak, bunu oturum verileri için + MongoDB, Kullanıcılar gibi diğer kalıcı verileri depolamak için kullanmanızı öneririm)

Siz de bazı API istekleri eklemek isteyebileceğinizden , aynı bağlantı noktasında hem HTTP hem de Web soketinin çalışması için http paketini de kullanacağız .


server.js

Aşağıdaki alıntı, yalnızca önceki teknolojileri kurmak için ihtiyacınız olan her şeyi içerir. Projelerimden birinde kullandığım server.js versiyonunun tamamını burada görebilirsiniz .

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

prizler / index.js

Bizim socketConnectionHandler, her şeyi server.js'nin içine koymaktan hoşlanmıyorum (mükemmel yapabilseniz bile), özellikle de bu dosya oldukça hızlı bir şekilde çok fazla kod içerebileceğinden.

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

Ekstra malzeme (müşteri):

JavaScript socket.io istemcisinin olabileceği basit bir sürüm:

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

Referanslar:

Kodun içine referans veremedim, bu yüzden buraya taşıdım.

1: Passport stratejilerinizi nasıl oluşturabilirsiniz: https://scotch.io/tutorials/easy-node-authentication-setup-and-local#handling-signupregistration


2

Bu makale ( http://simplapi.wordpress.com/2012/04/13/php-and-node-js-session-share-redi/ ) nasıl yapılacağını gösterir

  • HTTP sunucusunun oturumlarını Redis'te depolayın (Predis kullanarak)
  • bu oturumları bir çerezde gönderilen oturum kimliğiyle node.js'deki Redis'ten alın

Bu kodu kullanarak onları socket.io'da da alabilirsiniz.

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 arasında session ve redis kullan

// sunucu tarafı

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

Görünüşe göre, Node.js uç noktalarınızı doğrulamak için kullandığınız aynı kodu takarsanız (ancak istek nesnesini işlediğiniz herhangi bir parçayı ayarlamanız gerekir), yalnızca rotalarınız için jetonunuzu yeniden kullanabilirsiniz.
Nick Pineda

-5

bunu yapmalı

//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 benim durumumda
ZiTAL

8
Bu bir cevap verme girişimi bile değil. Bu kimlik doğrulama değil, sadece bağlantı kurmaktır.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.