Socket.io — autentifikatsiya, middleware
Muammodan boshlaylik
Chat ilovangiz tayyor. Lekin bir muammo bor:
Har kim istalgan xonaga kirishi mumkin. Hech qanday tekshiruv yo'q. Kim ekanligini bilmaysiz. "Ali" deb kim xohlasa o'zini tanishtirishi mumkin.
HTTP da bu oson hal qilinadi — JWT middleware, Authorization header. Lekin Socket.io da HTTP so'rov bir marta keladi (handshake paytida), keyin ulanish saqlanib qoladi. req va res yo'q. Qanday tekshirasiz?
Socket.io Middleware nima?
Socket.io o'zining middleware tizimiga ega. HTTP middleware ga o'xshaydi, lekin faqat ulanish paytida ishlaydi — har bir xabar uchun emas.
Foydalanuvchi Socket.io Server
| |
|--- handshake (auth) -->|
| |--- middleware 1 (auth tekshirish)
| |--- middleware 2 (log)
| |--- connection handler
|<-- ulandi -------------|
| |
|=== xabar yuborish =====>| ← bu yerda middleware qayta ishlamaydi
Middleware ulanish o'rnatilishidan oldin ishlaydi. Tokeni yaroqsiz bo'lsa — ulanish bekor qilinadi.
Hayotiy o'xshatma
Aeroport kirish nazorati. Samolyotga chiqishdan oldin — bitta tekshiruv punkti. Pasportingiz yo'q — kiritmaydi. Pasportingiz bor — ichiga o'tasiz. Ichkari kirgandan keyin har qadam uchun qayta tekshirilmaysiz.
Socket.io middleware ham shunday: bitta marta, ulanishda. Muvaffaqiyatli o'tsa — sessiya davomida to'liq huquqli.
JWT bilan autentifikatsiya
Ulanish paytida token yuborish
Mijoz JWT ni handshake paytida auth obyekti orqali yuboradi:
// Mijoz (React / JS)
import { io } from 'socket.io-client';
const token = localStorage.getItem('token'); // ← JWT
const socket = io('http://localhost:4000', {
auth: {
token, // ← handshake bilan birga ketadi
},
});
Server middleware
import jwt from 'jsonwebtoken';
io.use((socket, next) => {
const token = socket.handshake.auth?.token; // ← auth dan olish
if (!token) {
return next(new Error('Token yo\'q')); // ← ulanishni rad etish
}
try {
const payload = jwt.verify(token, process.env.JWT_SECRET);
socket.data.user = payload; // ← ma'lumotni socketga saqlash
next(); // ← davom etish
} catch (err) {
next(new Error('Token noto\'g\'ri')); // ← rad etish
}
});
io.on('connection', (socket) => {
// Endi foydalanuvchi ma'lumoti mavjud
console.log('Ulandi:', socket.data.user.email);
socket.on('xabar', (matn) => {
// socket.data.user — ishonchli ma'lumot (JWT dan)
io.emit('yangi-xabar', {
muallif: socket.data.user.name,
matn,
});
});
});
Mijozda xatoni qabul qilish
socket.on('connect_error', (err) => {
console.log('Ulanish xatosi:', err.message); // "Token yo'q" yoki "Token noto'g'ri"
if (err.message === 'Token yo\'q') {
// Login sahifasiga yo'naltirish
window.location.href = '/login';
}
});
Namespace middleware
Namespace darajasida middleware — faqat o'sha namespace ga tegishli:
const adminNs = io.of('/admin');
// Faqat admin namespace ga ulanuvchilar uchun
adminNs.use((socket, next) => {
const user = socket.data.user;
if (!user) {
return next(new Error('Autentifikatsiya talab qilinadi'));
}
if (user.role !== 'admin') {
return next(new Error('Ruxsat yo\'q')); // ← admin emas
}
next();
});
adminNs.on('connection', (socket) => {
console.log('Admin ulandi:', socket.data.user.email);
// admin funksiyalari...
});
Xona ruxsatini tekshirish
Middleware faqat ulanishda ishlaydi. Xonaga kirishda ham tekshirish kerak bo'lishi mumkin — bu connection handler ichida qilinadi:
io.on('connection', (socket) => {
socket.on('xonaga-kir', async ({ xonaId }) => {
const user = socket.data.user;
// Foydalanuvchi bu xonaga kirish huquqiga egami?
const ruxsat = await db.xonaAzolari.findOne({
where: { xonaId, userId: user.id },
});
if (!ruxsat) {
// Xatoni faqat shu socketga qaytarish
socket.emit('xato', { xabar: 'Bu xonaga kirish ruxsati yo\'q' });
return;
}
socket.join(xonaId);
socket.to(xonaId).emit('foydalanuvchi-kirdi', { ism: user.name });
});
});
Tez-tez uchraydigan xatolar
1. Middleware da next() ni chaqirmaslik
// ❌ — ulanish muzlab qoladi
io.use((socket, next) => {
const token = socket.handshake.auth?.token;
if (token) {
socket.data.user = jwt.verify(token, secret);
// next() yo'q — hech narsa bo'lmaydi!
}
});
// ✅
io.use((socket, next) => {
// ... tekshirish
next(); // ← har doim chaqirilishi kerak
});
2. socket.handshake.query va socket.handshake.auth aralashtirib yuborish
// ❌ — URL da token: ws://localhost:4000?token=abc
socket.handshake.query.token // ← URL da ko'rinadi, log larda ham
// ✅ — auth obektida
socket.handshake.auth.token // ← xavfsizroq, URL da ko'rinmaydi
3. Xona ruxsatini tekshirmaslik
// ❌ — har kim istalgan xonaga kirishi mumkin
socket.on('xonaga-kir', (xonaId) => {
socket.join(xonaId); // ← ruxsatsiz
});
// ✅
socket.on('xonaga-kir', async (xonaId) => {
const ruxsat = await tekshirRuxsat(socket.data.user.id, xonaId);
if (!ruxsat) return socket.emit('xato', { xabar: 'Ruxsat yo\'q' });
socket.join(xonaId);
});
Xulosa
- Socket.io middleware — ulanish bir marta tekshiriladi;
socket.datada saqlanadi socket.handshake.auth.token— JWT yuborish uchun xavfsiz yo'lnext(new Error(...))— ulanishni rad etish; mijozconnect_erroreventida qabul qiladi- Namespace middleware — faqat o'sha namespace ga tegishli
- Xona ruxsatini
socket.on('xonaga-kir')ichida alohida tekshiring
Keyingi dars: 45-dars: Server-Sent Events — bir tomonlama real-time