fix: 验证失败丢弃消息不发出 + 非法域名后缀拦截 + 验证码随机化 + 配置文件热更新

This commit is contained in:
2026-07-04 18:33:48 +08:00
parent cb37eceb9b
commit a716424dd5
3 changed files with 126 additions and 10 deletions
@@ -51,6 +51,9 @@ public class ChatFilterManager {
private int muteDuration = 300;
private boolean enabled = true;
// ★ 非法域名后缀列表(热更新)
private final Set<String> illegalDomainSuffixes = new HashSet<>();
// ============================================================
// ★ 新玩家验证码系统
// ============================================================
@@ -152,9 +155,13 @@ public class ChatFilterManager {
* 生成随机验证码:数学题或GUI随机50%概率
*/
public void generateMathVerification(String playerName) {
// 数学题验证码只抽一次,失败后不会再触发新的(用verifiedPlayers标记放行)
Random rand = sharedRandom.get();
generateMathChallenge(playerName, rand);
// 50% 概率数学题,50% 概率GUI
if (rand.nextBoolean()) {
generateMathChallenge(playerName, rand);
} else {
generateGUIChallenge(playerName, rand);
}
}
/** 生成数学题验证码 */
@@ -1169,6 +1176,78 @@ public class ChatFilterManager {
});
}
// ==================== 非法域名后缀管理 ====================
/**
* 加载非法域名后缀配置文件(支持热更新)
*/
public void loadIllegalDomains() {
illegalDomainSuffixes.clear();
File f = new File(plugin.getDataFolder(), "illegal_domains.txt");
if (!f.exists()) {
plugin.getLogger().info("[非法域名] 配置文件不存在,已加载默认内置后缀");
// 内置默认后缀
for (String s : new String[]{"com", "cn", "net", "org", "io", "xyz", "top", "club", "site", "online", "store", "tech", "info", "biz"}) {
illegalDomainSuffixes.add(s.toLowerCase());
}
return;
}
try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8))) {
String line;
while ((line = r.readLine()) != null) {
line = line.trim();
if (line.isEmpty() || line.startsWith("#")) continue;
// 去掉可能的 * 前缀
String suffix = line.replaceFirst("^\\*", "").toLowerCase();
if (!suffix.isEmpty()) {
illegalDomainSuffixes.add(suffix);
}
}
plugin.getLogger().info("[非法域名] 已加载 " + illegalDomainSuffixes.size() + " 个非法后缀");
} catch (Exception e) {
plugin.getLogger().warning("[非法域名] 加载失败: " + e.getMessage());
}
}
/**
* 检查名称是否包含非法域名格式(如 baidu_com, mysite.cn 等)
*/
public boolean containsIllegalDomain(String name) {
if (name == null || name.isEmpty()) return false;
String lowerName = name.toLowerCase();
// 查找第一个点后缀是否存在
int lastDot = lowerName.lastIndexOf('.');
if (lastDot < 0) return false;
String domainPart = lowerName.substring(lastDot + 1);
// 去掉可能的前缀(如 [xxx.abc_cn])
int bracketStart = domainPart.lastIndexOf('[');
if (bracketStart >= 0) {
domainPart = domainPart.substring(bracketStart + 1);
}
// 去掉可能的前缀(如 -prefix.)
if (domainPart.contains(".")) {
domainPart = domainPart.substring(domainPart.lastIndexOf('.') + 1);
}
// 检查后缀是否在非法列表中
for (String suffix : illegalDomainSuffixes) {
if (domainPart.equals(suffix)) {
// 还要检查整体域名结构,至少包含主域名部分
// 排除纯后缀如 ".com" 本身(前面没有主域名)
String beforeDot = lowerName.substring(0, lastDot);
if (!beforeDot.isEmpty()) {
return true;
}
}
}
return false;
}
/**
* 定时检查空闲玩家
*/
+10 -8
View File
@@ -366,6 +366,7 @@ public class Main extends JavaPlugin
treasureBridge.hook();
chatFilter = new ChatFilterManager(this);
chatFilter.loadConfig();
chatFilter.loadIllegalDomains(); // 加载非法域名后缀
welcome = new WelcomeManager(this);
// ===== 5.5 Web通信管理器 =====
@@ -1791,12 +1792,14 @@ public class Main extends JavaPlugin
String name = p.getName();
String ip = getPlayerIP(p);
// ===== 非法URL用户名拦截(最高优先级) =====
// ===== 非法URL/非法域名用户名拦截(最高优先级) =====
if (chatFilter != null && chatFilter.isEnabled()) {
List<String> urls = chatFilter.extractUrls(name);
if (!urls.isEmpty()) {
p.kickPlayer("§c§l禁止使用含URL格式的名称\n§7请修改您的游戏名称后再加入");
getLogger().warning("[安全] 玩家 " + name + " 因用户名包含非法URL被踢出");
boolean isIllegalUrl = !urls.isEmpty();
boolean isIllegalDomain = chatFilter.containsIllegalDomain(name);
if (isIllegalUrl || isIllegalDomain) {
p.kickPlayer("§c§l禁止使用含URL或非法域名的名称\n§7请修改您的游戏名称后再加入");
getLogger().warning("[安全] 玩家 " + name + " 因名称含非法URL/域名被踢出 (urls=" + isIllegalUrl + ", domain=" + isIllegalDomain + ")");
return;
}
}
@@ -2628,15 +2631,14 @@ public class Main extends JavaPlugin
e.setCancelled(true);
return;
} else if (checkResult == ChatFilterManager.VerificationResult.FAILED) {
// ★ 数学题回答错误:不再重新抽题(防止无限循环),直接放行
chatFilter.pendingMessages.put(p.getName(), msg);
// 重新触发验证,缓存当前消息
// ★ 回答错误:丢弃当前消息(不缓存不发出),重新抽题
chatFilter.pendingMessages.remove(p.getName()); // 清除之前的缓存
chatFilter.generateMathVerification(p.getName());
p.sendMessage("§c§l[验证码] §c回答错误,请重新回答");
e.setCancelled(true);
return;
}
// PENDING → GUI验证码,忽略聊天输入,让消息被缓存等待GUI点击
// PENDING → GUI验证码,忽略聊天输入,消息保持缓存
e.setCancelled(true);
return;
}
+35
View File
@@ -0,0 +1,35 @@
# 非法域名后缀列表
# 每行一个后缀,支持以 * 开头表示匹配所有子域名
# 修改此文件后无需重启,插件会在每次检测时重新加载
# 默认包含常见非法注册域名后缀
com
cn
net
org
io
xyz
top
club
site
online
store
tech
info
biz
ru
tk
ml
ga
cf
cc
ws
tv
link
click
date
vip
party
fun
zone
space