Аутентификация Socket.io
-
15-10-2019 - |
Вопрос
Я пытаюсь использовать socket.io в node.js и пытаюсь позволить серверу предоставить идентификацию каждому из клиентов Socket.io. Поскольку код сокета выходит за рамки кода HTTP -сервера, он не имеет легкого доступа к отправленной информации запроса, поэтому я предполагаю, что его необходимо отправить во время подключения. Какой лучший способ
1) Получите информацию на сервере о том, кто подключается через socket.io
2) Проверка подлинности, кто они говорят (я в настоящее время использую Express, если это облегчает облегчение)
Решение
Используйте Connect-REDIS и имейте Redis в качестве магазина сеансов для всех аутентифицированных пользователей. Убедитесь, что на аутентификации вы отправляете ключ (обычно req.sessionId) клиенту. Попросите клиента хранить этот ключ в файле cookie.
В Socket Connect (или в любое время) принесите этот ключ из файла 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;
});
}
});
});
Другие советы
Мне также понравился путь pusherapp делает частные каналы.
Уникальный идентификатор розетки генерируется и отправляется в браузер Pusher. Это отправляется в ваше заявление (1) через запрос AJAX, который разрешает пользователю получить доступ к каналу против вашей существующей системы аутентификации. В случае успеха ваше приложение возвращает строку авторизации в браузер, подписанный с вами Pusher Secret. Это отправляется толканию через WebSocket, который завершает авторизацию (2), если строка авторизации соответствует.
Потому что также socket.io
Имеет уникальный сокет_ид для каждого сокета.
socket.on('connect', function() {
console.log(socket.transport.sessionid);
});
Они использовали Подписанные строки авторизации авторизировать пользователей.
Я еще не отразил это socket.io
, но я думаю, что это может быть довольно интересной концепцией.
Я знаю, что это немного старое, но для будущих читателей в дополнение к подходу к разбору cookie и получению сеанса из хранилища (например. 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
});
Я написал более подробное объяснение этого метода и куки здесь.
Эта статья (http://simplapi.wordpress.com/2012/04/13/php-and-node-js-session-share-redi/) показывает, как
- Хранить сеансы HTTP -сервера в Redis (с помощью Predis)
- Получите эти сеансы от Redis in node.js от идентификатора сеанса, отправленного в cookie
Используя этот код, вы также можете получить их в 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));
});
});
Вот моя попытка выполнить следующую работу:
- выражать: 4.14
- сокет.io: 1.5
- заграничный пасспорт (Использование сеансов): 0,3
- Редис: 2.6 (Действительно быстрая структура данных для обработки сеансов; но вы также можете использовать другие, такие как MongoDB. Однако я призываю вас использовать это для данных сеанса + MongoDB для хранения других постоянных данных, таких как пользователи)
Так как вы, возможно, захотите добавить некоторые запросы API, мы также будем использовать http Пакет, чтобы как HTTP, так и Web Socket работают в одном порту.
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: Как настроить стратегии паспорта: https://scoth.io/tutorials/easy-node-authentication-setup-and-local#handling-signupregistration
Используйте сеанс и Redis между C/S
// Серверная сторона
io.use(function(socket, next) {
console.log(socket.handshake.headers.cookie); // get here session id 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)