mirror of
https://github.com/MCSManager/MCSManager.git
synced 2025-02-05 15:29:35 +08:00
230 lines
6.5 KiB
JavaScript
230 lines
6.5 KiB
JavaScript
const router = require("express")();
|
|
|
|
const TokenManager = require("../helper/TokenManager");
|
|
const { WebSocketObserver } = require("../model/WebSocketModel");
|
|
|
|
const permssion = require("../helper/Permission");
|
|
const response = require("../helper/Response");
|
|
const loginedContainer = require("../helper/LoginedContainer");
|
|
const counter = require("../core/counter");
|
|
|
|
require("express-ws")(router);
|
|
|
|
//WebSocket 会话类
|
|
class WebsocketSession {
|
|
constructor(config = {}) {
|
|
this.login = config.login || false;
|
|
this.uid = config.uid || null;
|
|
this.ws = config.ws || null;
|
|
this.username = config.username || null;
|
|
this.token = config.token || null;
|
|
this.console = config.console || null;
|
|
this.sessionID = config.sessionID || null;
|
|
}
|
|
send(data) {
|
|
if (data) response.wsSend(data.ws, data.resK, data.resV, data.body);
|
|
}
|
|
|
|
getWebsocket() {
|
|
return this.ws || null;
|
|
}
|
|
}
|
|
|
|
//判断当前令牌者是否在线
|
|
function isWsOnline(token) {
|
|
for (let k in MCSERVER.allSockets) {
|
|
let wsSession = MCSERVER.allSockets[k];
|
|
if (wsSession.token == token) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// 最高心跳包延迟
|
|
const MAX_ALIVE_COUNT = 60;
|
|
|
|
//WebSocket 创建
|
|
router.ws("/ws", function (ws, req) {
|
|
// 令牌
|
|
let token = req.query[permssion.tokenName] || null;
|
|
|
|
//无令牌 或 未登录
|
|
if (!token || !req.session["login"]) {
|
|
counter.plus("csrfCounter");
|
|
ws.close();
|
|
return;
|
|
}
|
|
|
|
token = token.trim();
|
|
let username = null;
|
|
let status = false;
|
|
let wsAliveHBCount = MAX_ALIVE_COUNT;
|
|
|
|
//临时的会话id 一般只用于内部验证是否是这个tcp链接
|
|
let uid = permssion.randomString(12) + Date.parse(new Date()).toString();
|
|
let session_id = req.sessionID;
|
|
|
|
MCSERVER.log("[ WS CREATE ] 新的 Ws 创建 会话ID: [", session_id, "]");
|
|
|
|
//从令牌管理器中 获取对应的用户
|
|
username = TokenManager.getToken(token);
|
|
|
|
//Token 任务完成 | 删除
|
|
TokenManager.delToken(token);
|
|
delete req.session["token"];
|
|
req.session.save();
|
|
|
|
//用户名检查
|
|
if (!username || typeof username != "string" || username.trim() == "") {
|
|
MCSERVER.warning("错误令牌的 WS 尝试建立链接 | 已经阻止", ["用户值:", username, " 令牌值:", token].join(" "));
|
|
counter.plus("notPermssionCounter");
|
|
ws.close();
|
|
return;
|
|
}
|
|
|
|
//唯一性检查
|
|
if (isWsOnline(token)) {
|
|
MCSERVER.warning("此令牌正在使用 | 阻止重复使用 | isWsOnline", ["用户值:", username, " 令牌值:", token].join(" "));
|
|
ws.close();
|
|
return;
|
|
}
|
|
|
|
username = username.trim();
|
|
|
|
//登录逻辑性缺陷检查
|
|
if (!loginedContainer.isLogined(session_id, username)) {
|
|
MCSERVER.warning("未经过登陆逻辑的用户尝试连接 | 已经阻止", ["用户值:", username, " 令牌值:", token].join(" "));
|
|
counter.plus("notPermssionCounter");
|
|
ws.close();
|
|
return;
|
|
}
|
|
|
|
//WebsocketSession 类生成
|
|
let WsSession = new WebsocketSession({
|
|
//Ws 判断身份条件,必须在 token 管理器与 Session 中认证登录
|
|
login: username && req.session["login"] ? true : false,
|
|
uid: uid,
|
|
sessionID: session_id,
|
|
ws: ws,
|
|
username: username,
|
|
token: token,
|
|
console: null
|
|
});
|
|
|
|
//Session 级别验证登录检查
|
|
if (!WsSession.login) {
|
|
MCSERVER.warning("不明身份者建立 ws 链接", "已经阻止 | 可能的用户值: " + WsSession.username);
|
|
counter.plus("notPermssionCounter");
|
|
ws.close();
|
|
return;
|
|
}
|
|
|
|
//状态标识
|
|
status = true;
|
|
|
|
//放置全局在线列表
|
|
MCSERVER.allSockets[uid] = WsSession;
|
|
if (!MCSERVER.onlineUser[WsSession.username]) MCSERVER.onlineUser[WsSession.username] = WsSession;
|
|
|
|
//检查通过..
|
|
MCSERVER.log("[ WebSocket INIT ]", " 用户:", username, "与服务器建立链接");
|
|
|
|
//数据到达事件
|
|
ws.on("message", function (data) {
|
|
try {
|
|
//是否合法用户检查
|
|
if (!WsSession.login) {
|
|
//触发这里代表极为有可能有人正在攻击你
|
|
MCSERVER.warning("没有登录的用户正在尝试发送 Ws 命令", "已经阻止 | 可能的用户值: " + username);
|
|
counter.plus("notPermssionCounter");
|
|
ws.close();
|
|
return;
|
|
}
|
|
|
|
//在线用户容器检查
|
|
if (!MCSERVER.onlineUser[username]) {
|
|
counter.plus("userOnlineCounter");
|
|
MCSERVER.onlineUser[username] = WsSession;
|
|
}
|
|
|
|
//检查完毕 | 开始解析数据
|
|
//自定义协议数据解析
|
|
let loc = data.indexOf("\n\n");
|
|
let reqHeader = data.substr(0, loc);
|
|
let reqBody = data.substr(loc + 2);
|
|
|
|
//Websocket 自定义协议解析
|
|
const reqHeaderObj = JSON.parse(reqHeader);
|
|
if (!reqHeaderObj) return;
|
|
|
|
//Websocket 心跳包 | 前端 10 秒递增链接健康指数
|
|
//当网络延迟特别高时,也能很好的降低指数. 将来指数够低时,将自动优化数据的发送
|
|
if (reqHeaderObj["RequestValue"] == "HBPackage") {
|
|
status = true;
|
|
// 最高心跳包健康数
|
|
wsAliveHBCount < MAX_ALIVE_COUNT && wsAliveHBCount++;
|
|
return;
|
|
}
|
|
|
|
WebSocketObserver().emit("ws/req", {
|
|
ws: ws,
|
|
req: req,
|
|
user: username,
|
|
header: reqHeaderObj,
|
|
body: reqBody,
|
|
RequestValue: reqHeaderObj["RequestValue"],
|
|
token: token,
|
|
WsSession: WsSession
|
|
});
|
|
} catch (err) {
|
|
MCSERVER.error("WebSocket 请求处理时异常:", err);
|
|
}
|
|
});
|
|
|
|
//关闭事件
|
|
ws.on("close", function () {
|
|
WebSocketClose();
|
|
});
|
|
|
|
//Websocket 心跳包检查 | 10 秒递减一个链接健康指数
|
|
var HBMask = setInterval(() => {
|
|
// 超过指定次数不响应,代表链接丢失
|
|
if (wsAliveHBCount <= 0) {
|
|
MCSERVER.log("[ WebSocket HBPackage ]", "用户", username, "长时间未响应心跳包 | 已自动断开");
|
|
WebSocketClose();
|
|
}
|
|
wsAliveHBCount--;
|
|
}, 1000 * 10);
|
|
|
|
//Websocket 关闭函数
|
|
function WebSocketClose() {
|
|
if (!status) return;
|
|
|
|
ws.close();
|
|
clearInterval(HBMask);
|
|
status = false;
|
|
|
|
//再删一次,保险
|
|
TokenManager.delToken(token);
|
|
delete req.session["token"];
|
|
WsSession = null;
|
|
req.session.save();
|
|
|
|
//释放全局变量
|
|
if (MCSERVER.onlineUser[username]) {
|
|
delete MCSERVER.onlineUser[username];
|
|
}
|
|
delete MCSERVER.allSockets[uid];
|
|
MCSERVER.log("[ WebSocket CLOSE ]", "用户", username, "已经断开链接");
|
|
}
|
|
});
|
|
|
|
//加载 ws 子路由
|
|
require("../core/tools").autoLoadModule("route/websocket/", "websocket/", (path) => {
|
|
require(path);
|
|
});
|
|
|
|
//模块导出
|
|
module.exports = router;
|