Node.js+Express+Socket.io:如何检测用户清除Cookie或会话超时?
刚好我之前也碰到过一模一样的场景——Express会话过期或者用户清了Cookie,但Socket连接还连着,导致在线状态和通知都出问题。咱们可以从绑定会话与Socket连接、服务器端主动检测、客户端主动校验这几个方向来解决,下面给你具体的思路和代码:
1. 先把Socket连接和Express会话绑定起来
Socket.io和Express的会话默认是独立的,所以第一步要让它们关联上,这样我们才能通过会话状态判断用户是否有效。
假设你用的是express-session,可以这样整合:
const express = require('express'); const session = require('express-session'); const http = require('http'); const { Server } = require('socket.io'); const app = express(); const server = http.createServer(app); // 配置Express Session const sessionMiddleware = session({ secret: 'your-secret-key', resave: false, saveUninitialized: false, cookie: { maxAge: 3600000 } // 1小时超时示例 }); // 把Session中间件挂载到Express app.use(sessionMiddleware); // 让Socket.io使用这个Session中间件 const io = new Server(server); io.use((socket, next) => { sessionMiddleware(socket.request, socket.request.res, next); }); // 用一个Map来存储SessionID和Socket实例的映射 const sessionSocketMap = new Map(); io.on('connection', (socket) => { const sessionId = socket.request.session.id; // 绑定Session和Socket sessionSocketMap.set(sessionId, socket); console.log(`User connected, session ID: ${sessionId}`); // 断开时移除映射 socket.on('disconnect', () => { sessionSocketMap.delete(sessionId); console.log('User disconnected'); }); });
2. 服务器端主动检测会话超时
现在会话和Socket绑定了,我们可以定时检查会话是否过期,一旦过期就主动断开Socket:
// 每隔30秒检查一次所有活跃的Socket对应的会话 setInterval(() => { for (const [sessionId, socket] of sessionSocketMap.entries()) { const currentSession = socket.request.session; // 检查会话是否存在,或者是否已过期 if (!currentSession || Date.now() > currentSession.cookie.expires.getTime()) { // 强制断开Socket socket.disconnect(true); sessionSocketMap.delete(sessionId); console.log(`Session expired for ${sessionId}, disconnected socket`); } } }, 30000);
如果你的Session是存在Redis里的(生产环境推荐),还可以订阅Redis的键过期事件,一旦会话对应的键过期,直接找到对应的Socket断开,这样更高效。
3. 客户端主动检测Cookie状态
用户手动清除Cookie的时候,服务器端没法立刻知道,所以客户端可以定时检查登录相关的Cookie是否存在,不存在就主动断开Socket:
<script src="/socket.io/socket.io.js"></script> <script> const socket = io(); // 每隔10秒检查一次Session Cookie(默认是connect.sid) setInterval(() => { const hasSessionCookie = document.cookie.includes('connect.sid'); if (!hasSessionCookie && socket.connected) { socket.disconnect(); console.log('Cookie cleared, disconnected socket'); // 这里还可以跳转到登录页之类的 } }, 10000); </script>
4. 增强Socket.io的Ping/Pong验证
Socket.io本身有心跳机制,我们可以在服务器端的Ping事件里加一层会话校验,确保每次心跳都验证用户的会话有效性:
io.on('connection', (socket) => { // 监听客户端的Ping请求 socket.on('ping', () => { const currentSession = socket.request.session; if (!currentSession || Date.now() > currentSession.cookie.expires.getTime()) { // 会话无效,直接断开 socket.disconnect(true); } else { // 会话有效,返回Pong socket.emit('pong'); } }); });
总结
把这几个方法结合起来,就能实现类似窗口关闭时的disconnect事件效果:
- 绑定会话和Socket,让两者状态关联
- 服务器定时检查会话,过期就断开Socket
- 客户端主动检测Cookie,清除后主动断开
- 利用心跳机制实时验证会话有效性
这样就能解决用户清Cookie或会话超时后,Socket还连着的问题,好友列表的在线状态也能同步更新了。
内容的提问来源于stack exchange,提问作者andrew1515
相关产品推荐
相关产品推荐

