fix: 验证失败丢弃消息不发出 + 非法域名后缀拦截 + 验证码随机化 + 配置文件热更新
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时检查空闲玩家
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user