Node.js Discord授权WebSocket Bot连接被拒:已获取有效Token但仍报“Missing token!”
Node.js Discord授权WebSocket Bot连接被拒:已获取有效Token但仍报“Missing token!”
从你的描述和日志来看,问题核心是:用户通过Discord OAuth2成功登录并获取了有效Token,但WebSocket连接始终因为会话未被识别而被拒绝(日志中Odrzucono połączenie WebSocket - użytkownik niezalogowany!翻译为“WebSocket连接被拒绝 - 用户未登录”)。以下是问题分析和分步修复方案:
问题根源诊断
- 会话Cookie未在WebSocket握手时发送:浏览器默认可能不会自动在WebSocket请求中携带HttpOnly Cookie,或Cookie配置存在问题。
- 内存会话存储不稳定:当前用
SESSIONS对象存储会话,服务器重启后所有会话会丢失,即使用户持有有效Cookie。 - Cookie解析逻辑鲁棒性不足:手动拆分Cookie的方式可能无法处理复杂的Cookie字符串格式。
- WebSocket服务器实例缺失:你提供的代码中缺少WebSocket服务器的初始化代码(可能是粘贴遗漏,但会导致
server.handleUpgrade报错)。 - Cookie属性不完整:未设置
SameSite等属性,可能导致浏览器在某些上下文下拒绝发送Cookie。
分步修复方案
1. 补全WebSocket服务器初始化
在引入ws模块后,添加WebSocket服务器实例:
const WebSocket = require("ws"); const server = new WebSocket.Server({ noServer: true }); // 新增:创建无服务器绑定的WebSocket实例
2. 优化会话Cookie配置
在handleAuthCallback中设置Cookie时,添加安全属性,确保Cookie能被WebSocket请求携带:
res.writeHead(302, { "Set-Cookie": `session=${sessionId}; Path=/; HttpOnly; SameSite=Strict; ${process.env.NODE_ENV === 'production' ? 'Secure' : ''}`, "Location": "/" });
SameSite=Strict:确保Cookie仅在同域请求中发送Secure:生产环境下仅通过HTTPS发送Cookie(需启用HTTPS)HttpOnly:防止XSS攻击,不影响WebSocket请求携带Cookie
3. 替换为express-session处理会话(更可靠)
手动会话管理容易出错,改用express-session来处理会话解析和存储:
// 初始化会话中间件 const sessionMiddleware = session({ secret: process.env.SESSION_SECRET || "your-strong-secret-key-here", // 生产环境用环境变量存储密钥 resave: false, saveUninitialized: false, cookie: { httpOnly: true, sameSite: "Strict", secure: process.env.NODE_ENV === "production", maxAge: 7 * 24 * 60 * 60 * 1000 // 与Discord Token有效期一致(7天) } }); // 在Express中使用会话中间件 app.use(sessionMiddleware);
然后修改handleAuthCallback,将用户ID存入express-session:
async function handleAuthCallback(req, res) { // ... 原有代码省略 ... console.log("User data:", userData); // 将用户ID存入会话 req.session.userId = userData.id; await new Promise((resolve, reject) => { req.session.save((err) => err ? reject(err) : resolve()); }); res.writeHead(302, { Location: "/" }); res.end(); }
4. 在WebSocket升级处理中使用会话中间件
由于WebSocket升级请求不经过Express路由,需手动调用会话中间件解析Cookie:
httpServer.on("upgrade", (request, socket, head) => { // 先通过会话中间件解析请求中的会话 sessionMiddleware(request, {}, () => { const pathname = url.parse(request.url).pathname?.replace(/\/$/, ""); if (pathname === "/server") { const userId = request.session.userId; if (!userId) { console.log("❌ Odrzucono połączenie WebSocket - użytkownik niezalogowany!"); return socket.destroy(); } console.log(`✅ Połączony użytkownik Discord ID: ${userId}`); server.handleUpgrade(request, socket, head, (ws) => { server.emit("connection", ws, request); }); } else { socket.destroy(); } }); });
5. 确保前端WebSocket请求携带凭证
如果前端是浏览器,创建WebSocket连接时显式指定携带凭证(同域下默认会携带,但显式设置更可靠):
// 前端JavaScript代码 const ws = new WebSocket("ws://localhost:PORT/server", { credentials: "include" });
6. 生产环境改用持久化会话存储
内存会话在服务器重启后会丢失,推荐使用Redis或MongoDB等持久化存储:
// 示例:使用Redis存储会话 const RedisStore = require("connect-redis")(session); const redis = require("redis"); const redisClient = redis.createClient({ url: process.env.REDIS_URL }); redisClient.connect().catch(console.error); const sessionMiddleware = session({ store: new RedisStore({ client: redisClient }), secret: process.env.SESSION_SECRET, resave: false, saveUninitialized: false, // 其他Cookie配置... });
验证修复效果
- 重启服务器,重新通过Discord登录
- 打开浏览器DevTools > 应用 > Cookie,确认
sessionCookie已设置 - 发起WebSocket连接,在网络面板中查看握手请求的
Cookie头是否包含session字段 - 检查服务器日志,确认是否输出
✅ Połączony użytkownik Discord ID: [用户ID]
备注:内容来源于stack exchange,提问作者Wojtek Szymański
相关产品推荐
相关产品推荐

