第32批:后台识别拉黑信号——sync_blacklist落表、validateWebAccess拉黑拦截、CDK兑换与前端拉黑提示
This commit is contained in:
+17
@@ -94,6 +94,15 @@ function cdkExchange($token) {
|
||||
|
||||
$db = getDB();
|
||||
|
||||
// ★ 第32批拉黑拦截:本地兑换前先查拉黑名单
|
||||
// (web_player_blacklist 由 Java BlacklistManager 经 sync_blacklist 同步;
|
||||
// 本地CDK存在也一律拒绝,与 Java CDKManager.redeem 的检查口径一致)
|
||||
$blackReason = playerWebBlacklistReason($player);
|
||||
if ($blackReason !== null) {
|
||||
@error_log("[cdk] 拉黑拦截兑换: player={$player}");
|
||||
error('你已被拉黑:' . $blackReason);
|
||||
}
|
||||
|
||||
// 查询CDK
|
||||
$stmt = $db->prepare("SELECT * FROM cdk WHERE code = :code");
|
||||
$stmt->bindValue(':code', strtoupper(trim($code)), SQLITE3_TEXT);
|
||||
@@ -456,6 +465,14 @@ function tryRemoteCdkValidation($db, $code, $player = '') {
|
||||
|
||||
@error_log("[cdk] 远程验证结果: status={$result['status']} amount={$result['amount']}");
|
||||
|
||||
// ★ 第32批:Java端拉黑拦截信号 → 直接提示被拉黑
|
||||
// (若当 null 返回,上层会误报「CDK不存在」,掩盖真实原因)
|
||||
if ($result['status'] === 'blacklisted') {
|
||||
$blReason = playerWebBlacklistReason($player);
|
||||
@error_log("[cdk] 远程验证 blacklisted: player={$player}");
|
||||
error('你已被拉黑:' . ($blReason !== null ? $blReason : '违反服务器规则'));
|
||||
}
|
||||
|
||||
if ($result['status'] === 'success') {
|
||||
// ★ Java端已标记used并加债券,Web端只做记录不重复加钱
|
||||
return [
|
||||
|
||||
@@ -366,6 +366,10 @@ switch ($action) {
|
||||
case 'sync_bans':
|
||||
syncBans();
|
||||
break;
|
||||
// ★ 第32批:拉黑名单同步(Java BlacklistManager → web_player_blacklist)
|
||||
case 'sync_blacklist':
|
||||
syncBlacklist();
|
||||
break;
|
||||
// ★ 第十六批:公开封禁公示(玩家端首页卡片数据源,无需SECRET,限流保护)
|
||||
case 'get_ban_board':
|
||||
getBanBoard();
|
||||
@@ -2817,6 +2821,12 @@ function apiValidateWebloginToken() {
|
||||
error('账号已被封禁:' . $banReason);
|
||||
}
|
||||
|
||||
// ★ 第32批拉黑拦截:被拉黑玩家禁止登录Web后台
|
||||
$blackReason = playerWebBlacklistReason($row['player_name'], $db);
|
||||
if ($blackReason !== null) {
|
||||
error('你已被拉黑:' . $blackReason);
|
||||
}
|
||||
|
||||
success(['player' => $row['player_name']], 'Token有效');
|
||||
} catch (\Throwable $e) {
|
||||
error('Token验证失败: ' . $e->getMessage());
|
||||
@@ -3210,6 +3220,12 @@ function webLoginRequest() {
|
||||
error('账号已被封禁:' . $banReason);
|
||||
}
|
||||
|
||||
// ★ 第32批拉黑拦截:被拉黑玩家禁止走密码登录
|
||||
$blackReason = playerWebBlacklistReason($player, $db);
|
||||
if ($blackReason !== null) {
|
||||
error('你已被拉黑:' . $blackReason);
|
||||
}
|
||||
|
||||
// 检查玩家是否已注册
|
||||
if (!isPlayerRegistered($player)) {
|
||||
error('玩家未注册,请先在游戏中注册');
|
||||
@@ -3641,6 +3657,14 @@ function webAccessCheck() {
|
||||
'registered' => $result['registered'] ?? false,
|
||||
'message' => $result['message']
|
||||
], 401);
|
||||
} elseif ($result['mode'] === 'blacklisted') {
|
||||
// ★ 第32批:玩家已被拉黑(Java BlacklistManager 同步)
|
||||
jsonResponse([
|
||||
'success' => false,
|
||||
'blacklisted' => true,
|
||||
'player' => $result['player'] ?? '',
|
||||
'message' => $result['message']
|
||||
], 401);
|
||||
} elseif ($result['mode'] === 'need_register') {
|
||||
// 玩家未注册
|
||||
jsonResponse([
|
||||
@@ -4332,6 +4356,12 @@ function sendEmailCode() {
|
||||
error('账号已被封禁:' . $banReason);
|
||||
}
|
||||
|
||||
// ★ 第32批拉黑拦截:被拉黑玩家禁止发送登录验证码
|
||||
$blackReason = playerWebBlacklistReason($player);
|
||||
if ($blackReason !== null) {
|
||||
error('你已被拉黑:' . $blackReason);
|
||||
}
|
||||
|
||||
// ★ 从数据库读取玩家绑定的邮箱(唯一可信来源)
|
||||
$db = getDB();
|
||||
$stmt = $db->prepare("SELECT email FROM users WHERE player_name = :player");
|
||||
@@ -4446,6 +4476,12 @@ function verifyEmailCode() {
|
||||
error('账号已被封禁:' . $banReason);
|
||||
}
|
||||
|
||||
// ★ 第32批拉黑拦截:被拉黑玩家禁止走邮箱验证码登录
|
||||
$blackReason = playerWebBlacklistReason($player);
|
||||
if ($blackReason !== null) {
|
||||
error('你已被拉黑:' . $blackReason);
|
||||
}
|
||||
|
||||
// ★ 从数据库读取玩家绑定的邮箱(唯一可信来源)
|
||||
$db = getDB();
|
||||
$stmt = $db->prepare("SELECT email FROM users WHERE player_name = :player");
|
||||
@@ -5560,6 +5596,57 @@ function syncBans() {
|
||||
success(['count' => count($bans), 'ip_blacklist' => $ipCount]);
|
||||
}
|
||||
|
||||
// ===== Java端推送拉黑名单到PHP(第32批)=====
|
||||
// 数据源唯一:Java BlacklistManager(黑名单.md),PHP 只读不写。
|
||||
// 全量覆盖式同步(DELETE + INSERT),已过期条目不入表。
|
||||
function syncBlacklist() {
|
||||
$secret = getParam('secret');
|
||||
if (!$secret || $secret !== SECRET_KEY) error('认证失败');
|
||||
|
||||
$blacksRaw = getParam('blacks', '[]');
|
||||
$blacks = json_decode($blacksRaw, true);
|
||||
if (!is_array($blacks)) error('blacks格式无效');
|
||||
|
||||
$db = getDB();
|
||||
$db->exec("CREATE TABLE IF NOT EXISTS web_player_blacklist (
|
||||
name TEXT PRIMARY KEY,
|
||||
reason TEXT DEFAULT '',
|
||||
time_text TEXT DEFAULT '',
|
||||
expire_ms INTEGER DEFAULT 0,
|
||||
created_ms INTEGER DEFAULT 0,
|
||||
synced_at INTEGER DEFAULT 0
|
||||
)");
|
||||
|
||||
$now = time();
|
||||
$nowMs = $now * 1000;
|
||||
$count = 0;
|
||||
try {
|
||||
$db->exec('BEGIN IMMEDIATE');
|
||||
$db->exec("DELETE FROM web_player_blacklist");
|
||||
$stmt = $db->prepare("INSERT OR REPLACE INTO web_player_blacklist (name, reason, time_text, expire_ms, created_ms, synced_at) VALUES (:n, :r, :t, :e, :c, :s)");
|
||||
foreach ($blacks as $b) {
|
||||
if (empty($b['name'])) continue;
|
||||
$exp = (int)($b['expire'] ?? 0);
|
||||
if ($exp > 0 && $exp <= $nowMs) continue; // 已过期不入表
|
||||
$stmt->bindValue(':n', (string)$b['name'], SQLITE3_TEXT);
|
||||
$stmt->bindValue(':r', (string)($b['reason'] ?? ''), SQLITE3_TEXT);
|
||||
$stmt->bindValue(':t', (string)($b['time'] ?? ''), SQLITE3_TEXT);
|
||||
$stmt->bindValue(':e', $exp, SQLITE3_INTEGER);
|
||||
$stmt->bindValue(':c', (int)($b['created'] ?? 0), SQLITE3_INTEGER);
|
||||
$stmt->bindValue(':s', $now, SQLITE3_INTEGER);
|
||||
$stmt->execute();
|
||||
$count++;
|
||||
}
|
||||
$db->exec('COMMIT');
|
||||
} catch (\Throwable $e) {
|
||||
try { $db->exec('ROLLBACK'); } catch (\Throwable $ignored) {}
|
||||
@error_log('[syncBlacklist] Error: ' . $e->getMessage());
|
||||
error('拉黑名单写入失败: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
success(['count' => $count]);
|
||||
}
|
||||
|
||||
/**
|
||||
* ★ 把Java推送的IP封禁全量同步到 web_ip_blacklist
|
||||
* 规则:当前封禁列表里的IP全部入表;表里已不存在于本次推送的IP自动移除(解封即放行)
|
||||
|
||||
@@ -44,7 +44,7 @@ function debugLog($message, $context = []) {
|
||||
// 本层只负责「按语句里的表名判侧别」,真正给哪个连接由 dbOpen() 按运行模式决定。
|
||||
|
||||
if (!function_exists('dbJavaTables')) {
|
||||
/** Java 专属表清单(api/sync.php 通信组,40 张,精确匹配不做前缀匹配) */
|
||||
/** Java 专属表清单(api/sync.php 通信组,41 张,精确匹配不做前缀匹配) */
|
||||
function dbJavaTables() {
|
||||
static $t = null;
|
||||
if ($t === null) {
|
||||
@@ -58,7 +58,8 @@ if (!function_exists('dbJavaTables')) {
|
||||
'web_admin_changes', 'web_area_config', 'web_area_lands',
|
||||
'web_area_permissions', 'web_area_shop', 'web_ip_blacklist',
|
||||
'web_login_confirmations', 'web_login_requests', 'web_login_verified',
|
||||
'web_player_bans', 'web_plugin_admins', 'web_register_requests',
|
||||
'web_player_bans', 'web_player_blacklist', 'web_plugin_admins',
|
||||
'web_register_requests',
|
||||
'web_service_providers', 'web_session_log', 'web_transactions',
|
||||
'web_user_group_members', 'web_user_groups', 'weblogin_credentials',
|
||||
'weblogin_tokens',
|
||||
@@ -3114,6 +3115,38 @@ function playerWebBanReason($player, $db = null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* ★ 第32批拉黑拦截:查询玩家是否处于【拉黑】生效期(Java BlacklistManager
|
||||
* 通过 sync_blacklist 同步的 web_player_blacklist)。
|
||||
* 返回 null = 未拉黑(或表不存在 → 放行);否则返回拉黑理由字符串(供前端提示)。
|
||||
* expire_ms 单位:Java 推送的是毫秒(BlackEntry.expiresMs),0 = 永久。
|
||||
*/
|
||||
function playerWebBlacklistReason($player, $db = null) {
|
||||
if ($player === null || $player === '') return null;
|
||||
try {
|
||||
if ($db === null) $db = getDB();
|
||||
// 表不存在 = 从未同步过拉黑名单 → 直接放行(避免读路径执行DDL)
|
||||
$exists = $db->querySingle("SELECT name FROM sqlite_master WHERE type='table' AND name='web_player_blacklist'");
|
||||
if (!$exists) return null;
|
||||
$stmt = $db->prepare("SELECT reason, expire_ms FROM web_player_blacklist WHERE LOWER(name) = LOWER(:p)");
|
||||
if (!$stmt) return null;
|
||||
$stmt->bindValue(':p', $player, SQLITE3_TEXT);
|
||||
$res = $stmt->execute();
|
||||
if (!$res) return null;
|
||||
$nowMs = time() * 1000;
|
||||
while ($row = $res->fetchArray(SQLITE3_ASSOC)) {
|
||||
$exp = (int)$row['expire_ms'];
|
||||
if ($exp === 0 || $exp > $nowMs) {
|
||||
$reason = trim((string)($row['reason'] ?? ''));
|
||||
return $reason !== '' ? $reason : '违反服务器规则';
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
@error_log('[playerWebBlacklistReason] ' . $e->getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function validateWebAccess($webToken, $action = 'view', $password = null, $ipAddress = null) {
|
||||
if (!$ipAddress) $ipAddress = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
|
||||
|
||||
@@ -3149,6 +3182,14 @@ function validateWebAccess($webToken, $action = 'view', $password = null, $ipAdd
|
||||
return ['ok' => false, 'mode' => 'denied', 'message' => '账号已被封禁:' . $banReason];
|
||||
}
|
||||
|
||||
// ★ 第32批拉黑拦截:被拉黑玩家禁止使用Web端(Java BlacklistManager 同步)
|
||||
$blackReason = playerWebBlacklistReason($playerName, $db);
|
||||
if ($blackReason !== null) {
|
||||
@error_log("[validateWebAccess] DENIED: player blacklisted. player=$playerName");
|
||||
return ['ok' => false, 'mode' => 'blacklisted', 'player' => $playerName,
|
||||
'message' => '你已被拉黑:' . $blackReason];
|
||||
}
|
||||
|
||||
// 在线玩家表(Java插件同步,这里确保表存在)
|
||||
try {
|
||||
$db->exec("CREATE TABLE IF NOT EXISTS online_players (player_name TEXT PRIMARY KEY, login_time INTEGER DEFAULT 0)");
|
||||
|
||||
+23
-1
@@ -531,6 +531,13 @@ if ($currentVersion !== $BUILD_VERSION) {
|
||||
document.getElementById('playerInfo').textContent = '请先在游戏内注册';
|
||||
if (callback) callback({ok: false, need_register: true, player: currentPlayer});
|
||||
showRegisterMessage(data.message || '请先在游戏内注册账号');
|
||||
} else if (data.blacklisted) {
|
||||
// ★ 第32批:玩家已被拉黑(Java BlacklistManager 同步)→ 明确提示,不按Token无效处理
|
||||
AUTHENTICATED = false;
|
||||
currentPlayer = data.player || currentPlayer;
|
||||
document.getElementById('playerInfo').textContent = '已被拉黑';
|
||||
if (callback) callback({ok: false, blacklisted: true, player: currentPlayer, message: data.message});
|
||||
showBlacklistedMessage(data.message || '你已被拉黑');
|
||||
} else {
|
||||
// Token无效 → 清除缓存,进入游客模式
|
||||
AUTHENTICATED = false;
|
||||
@@ -576,6 +583,21 @@ if ($currentVersion !== $BUILD_VERSION) {
|
||||
document.getElementById('modalOverlay').style.display = 'flex';
|
||||
}
|
||||
|
||||
// ★ 第32批:拉黑提示弹层(不清理本地缓存,保留玩家名用于展示)
|
||||
function showBlacklistedMessage(message) {
|
||||
document.getElementById('modalTitle').textContent = '账号已被拉黑';
|
||||
document.getElementById('modalBody').innerHTML = `
|
||||
<div style="text-align:center;padding:20px">
|
||||
<div style="font-size:48px;margin-bottom:16px">🚫</div>
|
||||
<p style="color:var(--red);font-size:14px;margin-bottom:20px">${message}</p>
|
||||
<div style="background:rgba(239,68,68,0.1);border:1px solid var(--red);border-radius:8px;padding:16px;margin-bottom:20px">
|
||||
<p style="color:var(--text);font-size:13px;margin:0">你已被服务器拉黑,禁止使用商城、CDK与充值功能。如有异议请联系管理员。</p>
|
||||
</div>
|
||||
<button class="btn" onclick="location.reload()" style="margin-top:8px">刷新页面</button>
|
||||
</div>`;
|
||||
document.getElementById('modalOverlay').style.display = 'flex';
|
||||
}
|
||||
|
||||
function showRegisterMessage(message) {
|
||||
document.getElementById('modalTitle').textContent = '注册新账号';
|
||||
document.getElementById('modalBody').innerHTML = `
|
||||
@@ -1526,7 +1548,7 @@ if ($currentVersion !== $BUILD_VERSION) {
|
||||
|
||||
if (!data.success) {
|
||||
// Token可能已失效或过期
|
||||
if (data.need_password || data.need_game_login || data.need_register) {
|
||||
if (data.need_password || data.need_game_login || data.need_register || data.blacklisted) {
|
||||
// 需要进一步验证,不视为token无效
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user