基于ws WebSocket的JWT认证、事件监听等技术问题咨询
关于ws WebSocket库的三个核心问题(附代码修复方案)
嘿,作为经常用ws库开发的人,我来帮你理清这几个问题,先从你卡住的upgrade事件说起!
1. JWT身份认证与upgrade事件未触发的问题
为什么你的upgrade事件没执行?
你的代码里,new WebSocket.Server({ port: 8080 })会让ws库自己创建一个独立的HTTP服务器,而不是复用你传入的server对象。这就导致你给原server绑定的upgrade事件永远不会被触发——因为客户端连接的是8080端口的ws服务器,不是你原来的HTTP服务器。
修复方案+JWT认证实现
把wss的配置改成挂载到传入的server上,去掉port选项,然后在upgrade事件里完成JWT验证:
// File 2 修改后的完整代码 const jwt = require('jsonwebtoken'); // 需要先安装jsonwebtoken:npm i jsonwebtoken module.exports = function(server) { const WebSocket = require('ws'); // 关键:去掉port,改用server选项挂载到现有HTTP服务器 const wss = new WebSocket.Server({ server: server }); server.on('upgrade', function(request, socket, head) { console.log('Upgraded'); // 现在这个日志会正常打印了! // 1. 从请求头获取JWT const authHeader = request.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { // 认证失败,直接关闭连接 socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n'); socket.destroy(); return; } const token = authHeader.split(' ')[1]; try { // 2. 验证JWT(替换成你的密钥和验证逻辑) const decodedUser = jwt.verify(token, 'your-jwt-secret-key'); // 3. 验证通过,完成连接升级 wss.handleUpgrade(request, socket, head, function(ws) { // 把用户信息挂载到ws对象上,后续业务逻辑可以直接用 ws.user = decodedUser; wss.emit('connection', ws, request); }); } catch (err) { // 验证失败,拒绝连接 socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n'); socket.destroy(); } }); wss.on('connection', function connection(ws) { console.log('客户端已连接,用户ID:', ws.user.id); ws.on('message', function incoming(message) { console.log('received: %s', message); }); }); };
2. 实现类似socket.io的事件类型监听
ws库本身没有内置的事件类型机制,它只提供了基础的message事件来接收所有客户端消息。我们可以通过自定义JSON消息格式来模拟socket.io的事件监听:
服务端实现
wss.on('connection', function connection(ws) { ws.on('message', function incoming(rawMessage) { try { // 把客户端发送的JSON字符串解析成对象 const message = JSON.parse(rawMessage); // 根据message.type分发到对应的处理函数 switch(message.type) { case 'chat': handleChat(ws, message.data); break; case 'join-room': handleJoinRoom(ws, message.data.roomId); break; default: console.log('未知事件类型:', message.type); } } catch (err) { console.log('消息解析失败:', err); } }); // 定义聊天事件的处理逻辑 function handleChat(ws, content) { console.log('收到聊天消息:', content); // 给客户端回复一个带类型的消息 ws.send(JSON.stringify({ type: 'chat-reply', data: `已收到你的消息:${content}` })); } // 定义加入房间的处理逻辑 function handleJoinRoom(ws, roomId) { console.log(`用户${ws.user.id}加入房间${roomId}`); // 这里可以实现房间管理、广播等逻辑 } });
客户端实现(示例)
const ws = new WebSocket('ws://localhost:3000'); // 发送带事件类型的消息 function sendChatMessage(content) { ws.send(JSON.stringify({ type: 'chat', data: content })); } // 监听服务端的回复事件 ws.onmessage = function(event) { const message = JSON.parse(event.data); if (message.type === 'chat-reply') { console.log('服务端回复:', message.data); } };
3. ws中路径的作用与使用场景
在创建WebSocket.Server时设置path选项,主要有以下几个核心用途:
- 区分同一端口下的多个WebSocket服务:比如你可以同时运行
/ws/chat(聊天服务)和/ws/notifications(通知服务),各自处理不同的业务,不会互相干扰。 - 与HTTP路由隔离:避免WebSocket请求和普通HTTP API请求混淆,比如你的HTTP服务器有
/api/user这样的路由,设置WebSocket路径为/ws可以明确区分两类请求。 - 精细化权限控制:可以在
upgrade事件中通过request.url判断路径,对不同路径的WebSocket连接应用不同的认证或限流规则。
使用示例
// 在同一HTTP服务器上创建两个不同路径的WebSocket服务 const wssChat = new WebSocket.Server({ server: server, path: '/ws/chat' }); const wssNotifications = new WebSocket.Server({ server: server, path: '/ws/notifications' }); // 各自处理连接事件 wssChat.on('connection', (ws) => { console.log('聊天服务新连接'); }); wssNotifications.on('connection', (ws) => { console.log('通知服务新连接'); });
客户端连接时需要指定对应路径:
const chatWs = new WebSocket('ws://localhost:3000/ws/chat'); const notificationWs = new WebSocket('ws://localhost:3000/ws/notifications');
内容的提问来源于stack exchange,提问作者Jessica
相关产品推荐
相关产品推荐

