From 325d363f4b3a142147d19ba84e4e46caaad1c3ac Mon Sep 17 00:00:00 2001 From: youpaishidifu <1@ypshidifu.cn> Date: Mon, 26 Oct 2026 19:20:05 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AC=AC57=E6=89=B9=E7=BB=AD10=EF=BC=9Awebbind?= =?UTF-8?q?=E7=BB=91=E9=82=AE=E7=AE=B1=E5=AF=86=E7=A0=81=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E6=8E=A5=E5=85=A5=E7=BB=9F=E4=B8=80=E9=A3=8E=E6=8E=A7+?= =?UTF-8?q?=E7=A9=BA=E8=A1=8C=E6=B8=85=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Main.java: /webbind approve 密码验证从独立webbindPwdFails内存计数器 接入RiskControlManager(与/login完全一致):密码错5次→Bukkit真实封禁 5分钟+踢出+报警邮件+重连拒绝+到期自动解封 - Main.java: 删除已无引用的webbindPwdFails字段 - RiskControlManager.java: 按8a693e规则清理方法内部空行(11行) --- src/main/java/Sdf1_login/Main.java | 39 ++++++++++++------- .../java/Sdf1_login/RiskControlManager.java | 11 ------ 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/main/java/Sdf1_login/Main.java b/src/main/java/Sdf1_login/Main.java index 65edfa5..410f60b 100644 --- a/src/main/java/Sdf1_login/Main.java +++ b/src/main/java/Sdf1_login/Main.java @@ -142,7 +142,6 @@ public class Main extends JavaPlugin new HashMap<>(); private static final long IP_SKIP_MS = 300000L; // ★ 第56批:/webbind 批准密码验证失败计数(玩家名 -> 失败次数) - private final ConcurrentHashMap webbindPwdFails = new ConcurrentHashMap<>(); // ★ 第57批续5: /webbind approve 改聊天框输入密码(玩家名 -> 请求ID) private final ConcurrentHashMap pendingWebbindApprove = new ConcurrentHashMap<>(); private SalesStatsManager salesStats; @@ -6271,28 +6270,36 @@ public class Main extends JavaPlugin "reject", "", status -> {}); return; } - // 密码验证 - int failCount2 = webbindPwdFails.getOrDefault(p.getName(), 0); - if (failCount2 >= 5) { + // ★ 第57批续10:接入统一风控(RiskControlManager),与 /login 完全一致 + // 密码错5次 → Bukkit真实封禁5分钟 + 踢出 + 报警邮件(不再仅提示) + if (riskControl != null && riskControl.isBanned(p.getName())) { + int rem2 = riskControl.getBanRemainingSeconds(p.getName()); pendingWebbindApprove.remove(p.getName()); - p.sendMessage("§c§l[风控] §f连续5次失败,已被临时限制"); + p.sendMessage("§c§l[风控] §f密码错误次数过多,账号已被临时封禁 §e" + + rem2 + " §f秒"); return; } String pwdSalt2 = (String) db.getField(p.getName(), "password_salt"); String pwdHash2 = PasswordUtils.hash(input, pwdSalt2); String pwdResult2 = db.checkPasswordWithFallback(p.getName(), pwdHash2); if (pwdResult2 == null) { - failCount2++; - webbindPwdFails.put(p.getName(), failCount2); - p.sendMessage("§c密码错误(" + failCount2 + "/5)"); - if (failCount2 >= 5) { - p.sendMessage("§c§l[风控] §f连续5次失败,已被临时限制"); + int remaining2 = riskControl != null + ? riskControl.recordFailAttempt(p) : 4; + if (remaining2 < 0) { + // 已达阈值:triggerBan 已真实封禁并踢出 + return; + } + p.sendMessage("§c密码错误"); + if (remaining2 > 0) { + p.sendMessage("§c§l[风控] §f剩余 §e" + remaining2 + " §f次尝试机会"); + } else { + p.sendMessage("§c§l[风控] §f连续5次失败,已被临时封禁"); pendingWebbindApprove.remove(p.getName()); } return; } - // 验证通过,清零计数 - webbindPwdFails.remove(p.getName()); + // 验证通过,重置风控计数 + if (riskControl != null) riskControl.onLoginSuccess(p.getName()); String reqId2 = pendingBindReq; pendingWebbindApprove.remove(p.getName()); p.sendMessage("§e正在提交批准…"); @@ -9710,9 +9717,11 @@ public class Main extends JavaPlugin // ★ 第56批:approve 时需要密码(Java端直接验证,不再依赖PHP复核) if (bindApprove) { // ★ 第57批续5: 改聊天框输入密码(不再要求命令行参数密码) - int failCount = webbindPwdFails.getOrDefault(wp.getName(), 0); - if (failCount >= 5) { - wp.sendMessage("§c§l[风控] §f密码验证失败次数过多,已被临时限制。请稍后再试"); + // ★ 第57批续10:接入统一风控,封禁期内直接拒绝 + if (riskControl != null && riskControl.isBanned(wp.getName())) { + int rem = riskControl.getBanRemainingSeconds(wp.getName()); + wp.sendMessage("§c§l[风控] §f密码错误次数过多,账号已被临时封禁 §e" + + rem + " §f秒,请稍后再试"); return true; } pendingWebbindApprove.put(wp.getName(), bindReqId); diff --git a/src/main/java/Sdf1_login/RiskControlManager.java b/src/main/java/Sdf1_login/RiskControlManager.java index 0f84ce0..2dcc511 100644 --- a/src/main/java/Sdf1_login/RiskControlManager.java +++ b/src/main/java/Sdf1_login/RiskControlManager.java @@ -88,23 +88,18 @@ public class RiskControlManager { public int recordFailAttempt(Player player) { String name = player.getName(); int maxAttempts = getMaxFailAttempts(); - // 已被封禁,直接返回 if (isBanned(name)) { return -1; } - int current = failAttempts .getOrDefault(name, 0) + 1; failAttempts.put(name, current); - int remaining = Math.max(0, maxAttempts - current); - // 达到阈值 → 触发封禁 if (current >= maxAttempts) { triggerBan(player); } - return remaining; } @@ -117,16 +112,13 @@ public class RiskControlManager { long durationMs = getBanDurationMs(); long until = System.currentTimeMillis() + durationMs; banUntil.put(name, until); - String ip = plugin.getPlayerIP(player); int banMinutes = (int) (durationMs / 60000); - plugin.getLogger().warning( "[风控] 玩家 " + name + " 密码错误达到阈值(" + getMaxFailAttempts() + "次)" + ",封禁 " + banMinutes + " 分钟"); - // ★ 真实封禁:调用Bukkit BanList API阻止玩家重新加入服务器 String banReason = "§c§l[安全风控] 密码连续错误" + getMaxFailAttempts() + "次," @@ -145,7 +137,6 @@ public class RiskControlManager { } // 踢出玩家(无论BanList是否成功,立即踢出) player.kickPlayer(banReason); - // 异步发送报警邮件 sendBanAlertEmail(name, ip, banMinutes); } @@ -164,7 +155,6 @@ public class RiskControlManager { + " 未绑定邮箱,跳过报警邮件"); return; } - String subject = "[安全警告] 您的账号已被临时封禁"; String body = "尊敬的玩家 " + playerName + ":\n\n" @@ -187,7 +177,6 @@ public class RiskControlManager { + "如果非本人操作,建议您立即修改密码" + "(使用 /sdf1_login pw)。\n\n" + "--Sdf1_login 安全系统"; - Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { try { plugin.getEmail().sendBody(