From f5a3dfbda5b3a0da59cd5e0c76c3d9a02cece800 Mon Sep 17 00:00:00 2001 From: youpaishidifu Date: Fri, 17 Jul 2026 19:40:01 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20AreaGUIManager=E7=A1=AC=E7=BC=96?= =?UTF-8?q?=E7=A0=81URL=E6=94=B9=E4=B8=BA=E4=BB=8E=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E8=AF=BB=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AreaGUIManager删除用户组时通知PHP同步,URL和密钥之前写死为caoyuan.ypshidifu.cn - 现在改为从webManager.getWebBaseUrl()和getSecretKey()读取,支持其他服主自定义web后端 - extractCodeFromInput增强支持#code=fragment形式(上一轮修复) - verifyMinecraftAuthCode加debug日志便于排查 - deploy_sftp.py备份后缀改为.jar.old/.old.N(不以.jar结尾,避免MC加载冲突) - .gitignore添加hs_err_pid*.log --- .gitignore | 2 +- deploy_sftp.py | 127 +++++++++++++++++++ src/main/java/Sdf1_login/AreaGUIManager.java | 7 +- src/main/java/Sdf1_login/Main.java | 22 +++- src/main/java/Sdf1_login/WebManager.java | 14 +- 5 files changed, 162 insertions(+), 10 deletions(-) create mode 100644 deploy_sftp.py diff --git a/.gitignore b/.gitignore index 3599fd1..9e34dd6 100644 --- a/.gitignore +++ b/.gitignore @@ -81,4 +81,4 @@ lpt5 lpt6 lpt7 lpt8 -lpt9 \ No newline at end of file +lpt9hs_err_pid*.log diff --git a/deploy_sftp.py b/deploy_sftp.py new file mode 100644 index 0000000..bbca8bb --- /dev/null +++ b/deploy_sftp.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +""" +SFTP deployment script for Minecraft plugin JAR +Uploads JAR to sfe4-connect.simpfun.cn:2046 +""" + +import paramiko +import os +import sys +from pathlib import Path + +# SFTP Configuration +SFTP_HOST = "sfe4-connect.simpfun.cn" +SFTP_PORT = 2046 +SFTP_USER = "sfe3192491.df7d2f7f" +SFTP_PASS = "5350427807" + +# Remote paths +REMOTE_JAR_PATH = "/plugins/Sdf1_login.jar" + +# Local JAR path +LOCAL_JAR_PATH = r"D:\桌面\源码\mc_plugn\Sdf1_login\target\Sdf1_login.jar" + +def backup_remote_file(sftp, remote_path): + """Backup existing file by renaming to .old.N""" + try: + # Check if file exists + sftp.stat(remote_path) + + # Find next available .jar.old / .jar.old.N suffix + # ★ 备份名必须脱离 .jar 后缀(如 Sdf1_login.jar.old / .old.1), + # 否则 MC 服务器会加载所有 .jar 文件导致插件冲突。 + base, ext = os.path.splitext(remote_path) # base="/plugins/Sdf1_login", ext=".jar" + suffix = 1 + while True: + backup_path = f"{base}{ext}.old{f'.{suffix}' if suffix > 1 else ''}" + try: + sftp.stat(backup_path) + suffix += 1 + except FileNotFoundError: + # Found available backup name + sftp.rename(remote_path, backup_path) + print(f" Backed up existing file to: {backup_path}") + return True + except FileNotFoundError: + # File doesn't exist, no backup needed + return True + except Exception as e: + print(f" Warning: Could not backup file: {e}") + return False + +def main(): + print("=" * 60) + print("SFTP Deployment to sfe4-connect.simpfun.cn") + print("=" * 60) + + # Check if local JAR exists + if not os.path.exists(LOCAL_JAR_PATH): + print(f"❌ Local JAR not found: {LOCAL_JAR_PATH}") + return 1 + + print(f"\nLocal JAR: {LOCAL_JAR_PATH}") + print(f"Remote path: {REMOTE_JAR_PATH}") + + try: + # Create SSH client + print(f"\nConnecting to SFTP: {SFTP_HOST}:{SFTP_PORT}") + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + # Connect + ssh.connect( + hostname=SFTP_HOST, + port=SFTP_PORT, + username=SFTP_USER, + password=SFTP_PASS, + timeout=30 + ) + + # Open SFTP session + sftp = ssh.open_sftp() + print("Connected successfully!") + + # Backup existing JAR + print("\nBacking up existing JAR...") + backup_remote_file(sftp, REMOTE_JAR_PATH) + + # Upload new JAR + print("\nUploading new JAR...") + sftp.put(LOCAL_JAR_PATH, REMOTE_JAR_PATH) + print(f" Uploaded: {REMOTE_JAR_PATH}") + + # Verify upload + remote_stat = sftp.stat(REMOTE_JAR_PATH) + local_size = os.path.getsize(LOCAL_JAR_PATH) + + print(f"\nVerification:") + print(f" Local size: {local_size} bytes") + print(f" Remote size: {remote_stat.st_size} bytes") + + if remote_stat.st_size == local_size: + print(" ✅ Size match - upload verified!") + else: + print(" ⚠️ Size mismatch - upload may be incomplete") + + # Close connections + sftp.close() + ssh.close() + + print("\n" + "=" * 60) + print("✅ SFTP deployment completed successfully!") + print("=" * 60) + + return 0 + + except paramiko.AuthenticationException: + print("\n❌ Authentication failed. Check username/password.") + return 1 + except paramiko.SSHException as e: + print(f"\n❌ SSH connection failed: {e}") + return 1 + except Exception as e: + print(f"\n❌ Deployment failed: {e}") + return 1 + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/src/main/java/Sdf1_login/AreaGUIManager.java b/src/main/java/Sdf1_login/AreaGUIManager.java index e397065..00027f9 100644 --- a/src/main/java/Sdf1_login/AreaGUIManager.java +++ b/src/main/java/Sdf1_login/AreaGUIManager.java @@ -1337,15 +1337,16 @@ public class AreaGUIManager implements Listener { String groupName = names.get(raw); if (p.hasPermission("sdf1.admin")) { ugm.deleteGroupConfig(groupName); - // ★ 通知PHP同步 + // ★ 通知PHP同步(从插件配置读取地址和密钥,不写死) try { - java.net.URL url = new java.net.URL("https://caoyuan.ypshidifu.cn/plugin/api/land_api.php"); + String landApiUrl = plugin.webManager.getWebBaseUrl() + "/api/land_api.php"; + java.net.URL url = new java.net.URL(landApiUrl); java.net.HttpURLConnection conn = (java.net.HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setDoOutput(true); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); - String body = "action=delete_user_group&name=" + groupName + "&secret=sdf1_web_comm_2026_ypshidifu"; + String body = "action=delete_user_group&name=" + groupName + "&secret=" + java.net.URLEncoder.encode(plugin.webManager.getSecretKey(), "UTF-8"); java.io.OutputStream os = conn.getOutputStream(); os.write(body.getBytes()); os.flush(); diff --git a/src/main/java/Sdf1_login/Main.java b/src/main/java/Sdf1_login/Main.java index 5823db3..a6f248e 100644 --- a/src/main/java/Sdf1_login/Main.java +++ b/src/main/java/Sdf1_login/Main.java @@ -1612,12 +1612,26 @@ public class Main extends JavaPlugin input = input.trim(); // 如果包含code=参数,从中提取 - if (input.contains("code=") || input.startsWith("http")) { + if (input.contains("code=") || input.startsWith("http") || input.contains("#")) { try { - // 尝试解析URL中的query参数 + // ★ 优先处理 #code= fragment形式(Microsoft公共客户端OAuth返回格式) + // URL如: https://login.live.com/oauth20_desktop.srf#code=M.C512_BAY.2_xxx + if (input.contains("#code=")) { + int hashIdx = input.indexOf("#code="); + String afterHash = input.substring(hashIdx + 6); // "#code=" 之后的内容 + // code到下一个&或字符串结尾 + int ampIdx = afterHash.indexOf('&'); + String rawCode = ampIdx >= 0 ? afterHash.substring(0, ampIdx) : afterHash; + return java.net.URLDecoder.decode(rawCode, StandardCharsets.UTF_8); + } + + // 尝试解析URL中的query参数 (?code=xxx) int qIdx = input.indexOf('?'); if (qIdx >= 0) { String query = input.substring(qIdx + 1); + // 去掉fragment部分 + int hashQIdx = query.indexOf('#'); + if (hashQIdx >= 0) query = query.substring(0, hashQIdx); for (String param : query.split("&")) { String[] kv = param.split("=", 2); if (kv.length == 2 && kv[0].equals("code")) { @@ -1626,11 +1640,11 @@ public class Main extends JavaPlugin } } } catch (Exception e) { - // 解析失败,继续尝试其他方式 + getLogger().warning("[正版验证] extractCodeFromInput解析异常: " + e.getMessage()); } } - // 直接作为授权码返回 + // 直接作为授权码返回(纯code) return input; } diff --git a/src/main/java/Sdf1_login/WebManager.java b/src/main/java/Sdf1_login/WebManager.java index 3e84408..5521984 100644 --- a/src/main/java/Sdf1_login/WebManager.java +++ b/src/main/java/Sdf1_login/WebManager.java @@ -5162,7 +5162,14 @@ public class WebManager { } String sessionId = sessionData[0]; - String secret = java.net.URLEncoder.encode(secretKey, StandardCharsets.UTF_8); + String secret = secretKey; // ★ 不再URLEncoder.encode,直接发送原始密钥 + + // ★ 调试日志:记录code提取结果 + plugin.getLogger().info("[正版验证] /mscode 提交 - 玩家: " + playerName); + plugin.getLogger().info("[正版验证] sessionId: " + sessionId); + plugin.getLogger().info("[正版验证] code(前80字符): " + code.substring(0, Math.min(80, code.length()))); + plugin.getLogger().info("[正版验证] code长度: " + code.length()); + plugin.getLogger().info("[正版验证] secret: " + secret); webExecutor.submit(() -> { try { @@ -5174,7 +5181,10 @@ public class WebManager { postData.addProperty("code", code); postData.addProperty("secret", secret); - String response = doPost(url, postData.toString()); + String postBody = postData.toString(); + plugin.getLogger().info("[正版验证] POST body(前200字符): " + postBody.substring(0, Math.min(200, postBody.length()))); + + String response = doPost(url, postBody); if (response == null) { player.sendMessage("§c无法连接Web后端"); return;