From c3d2f81e9e6ae8d5cb0827536038242e59bcd233 Mon Sep 17 00:00:00 2001 From: youpaishidifu Date: Fri, 17 Jul 2026 14:02:08 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0/mscode=E5=91=BD=E4=BB=A4?= =?UTF-8?q?=EF=BC=9A=E6=94=AF=E6=8C=81=E7=B2=98=E8=B4=B4Microsoft=20OAuth?= =?UTF-8?q?=E6=8E=88=E6=9D=83=E7=A0=81=EF=BC=88=E5=85=AC=E5=85=B1=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=AB=AF=E6=B5=81=E7=A8=8B=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Sdf1_login/Main.java | 33 ++++++++ src/main/java/Sdf1_login/WebManager.java | 95 ++++++++++++++++++++++-- src/main/resources/plugin.yml | 3 + 3 files changed, 125 insertions(+), 6 deletions(-) diff --git a/src/main/java/Sdf1_login/Main.java b/src/main/java/Sdf1_login/Main.java index d8f178b..a15fd43 100644 --- a/src/main/java/Sdf1_login/Main.java +++ b/src/main/java/Sdf1_login/Main.java @@ -456,6 +456,8 @@ public class Main extends JavaPlugin getCommand("mslogin").setExecutor(this); if (getCommand("正版") != null) getCommand("正版").setExecutor(this); + if (getCommand("mscode") != null) + getCommand("mscode").setExecutor(this); if (getCommand("签到") != null) getCommand("签到") .setExecutor(this); @@ -5996,6 +5998,37 @@ public class Main extends JavaPlugin return true; } + // ===== /mscode <授权码>:粘贴Microsoft OAuth授权码 ===== + if (cmdName.equals("mscode")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + if (loggedIn.contains(p.getName())) { + p.sendMessage("§c您已登录,无需重复验证"); + return true; + } + if (args.length < 1) { + p.sendMessage("§c用法: /mscode <授权码>"); + p.sendMessage("§7从Microsoft登录页面复制授权码后粘贴到这里"); + return true; + } + String code = args[0].trim(); + if (code.length() < 10) { + p.sendMessage("§c授权码格式不正确,请复制完整的授权码"); + return true; + } + // 检查是否有进行中的会话 + if (webManager == null || !webManager.isMinecraftAuthPending(p.getName())) { + p.sendMessage("§c您没有进行中的验证会话,请先执行 /mslogin 发起验证"); + return true; + } + p.sendMessage("§e正在提交授权码验证..."); + webManager.verifyMinecraftAuthCode(p, code); + return true; + } + // /找回密码 if (cmdName.equals("找回密码")) { if (!(sender instanceof Player)) { diff --git a/src/main/java/Sdf1_login/WebManager.java b/src/main/java/Sdf1_login/WebManager.java index ec6644b..34a3c75 100644 --- a/src/main/java/Sdf1_login/WebManager.java +++ b/src/main/java/Sdf1_login/WebManager.java @@ -4955,30 +4955,49 @@ public class WebManager { com.google.gson.JsonObject data = json.getAsJsonObject("data"); String sessionId = data.get("session_id").getAsString(); String authUrl = data.get("auth_url").getAsString(); + String pasteUrl = data.has("paste_url") ? data.get("paste_url").getAsString() : null; // 记录会话 minecraftAuthSessions.put(playerName, new String[]{sessionId, String.valueOf(System.currentTimeMillis())}); // 在主线程发送消息 + final String finalPasteUrl = pasteUrl; Bukkit.getScheduler().runTask(plugin, () -> { if (player.isOnline()) { player.sendMessage("§6§l===== 正版验证 ====="); - player.sendMessage("§7请点击下方链接完成Microsoft账号验证:"); - player.sendMessage("§b" + authUrl); + player.sendMessage("§7方式一: 点击链接完成验证后,复制授权码粘贴到聊天:"); + player.sendMessage("§b/mscode <授权码>"); + player.sendMessage("§7方式二: 在浏览器中打开验证页面:"); + if (finalPasteUrl != null) { + player.sendMessage("§b" + finalPasteUrl); + } + player.sendMessage("§7步骤: 打开链接 → 登录Microsoft → 复制授权码 → /mscode粘贴"); player.sendMessage("§7链接10分钟内有效"); - player.sendMessage("§7验证完成后将自动登录服务器"); player.sendMessage("§6§l===================="); - // 尝试发送可点击的URL + // 尝试发送可点击的URL(授权链接) try { net.kyori.adventure.text.Component msg = net.kyori.adventure.text.Component.text() - .content("§7[§b点击打开验证页面§7]") + .content("§7[§b点击打开Microsoft登录页面§7]") .clickEvent(net.kyori.adventure.text.event.ClickEvent.openUrl(authUrl)) - .hoverEvent(net.kyori.adventure.text.event.HoverEvent.showText(net.kyori.adventure.text.Component.text("§e点击在浏览器中打开验证页面"))) + .hoverEvent(net.kyori.adventure.text.event.HoverEvent.showText(net.kyori.adventure.text.Component.text("§e点击在浏览器中打开Microsoft登录"))) .build(); player.sendMessage(msg); } catch (Exception e) { // 低版本不支持Adventure API,忽略 } + // 可点击的粘贴页面 + if (finalPasteUrl != null) { + try { + net.kyori.adventure.text.Component pasteMsg = net.kyori.adventure.text.Component.text() + .content("§7[§b点击打开粘贴授权码页面§7]") + .clickEvent(net.kyori.adventure.text.event.ClickEvent.openUrl(finalPasteUrl)) + .hoverEvent(net.kyori.adventure.text.event.HoverEvent.showText(net.kyori.adventure.text.Component.text("§e点击打开粘贴授权码页面"))) + .build(); + player.sendMessage(pasteMsg); + } catch (Exception e) { + // 低版本不支持Adventure API,忽略 + } + } } }); @@ -5115,6 +5134,70 @@ public class WebManager { minecraftAuthSessions.remove(playerName); } + /** + * 验证玩家粘贴的Microsoft OAuth授权码(/mscode命令调用) + * 将授权码发送给PHP后端的verify_code端点 + */ + public void verifyMinecraftAuthCode(Player player, String code) { + String playerName = player.getName(); + String[] sessionData = minecraftAuthSessions.get(playerName); + if (sessionData == null) { + player.sendMessage("§c没有找到您的验证会话,请先执行 /mslogin"); + return; + } + + String sessionId = sessionData[0]; + String secret = java.net.URLEncoder.encode(secretKey, StandardCharsets.UTF_8); + + webExecutor.submit(() -> { + try { + String url = webBaseUrl + "/api/minecraft_auth.php?action=verify_code"; + + // POST参数 + String postData = "session_id=" + java.net.URLEncoder.encode(sessionId, StandardCharsets.UTF_8) + + "&code=" + java.net.URLEncoder.encode(code, StandardCharsets.UTF_8) + + "&secret=" + secret; + + String response = doPost(url, postData); + if (response == null) { + player.sendMessage("§c无法连接Web后端"); + return; + } + + com.google.gson.JsonObject json = com.google.gson.JsonParser.parseString(response).getAsJsonObject(); + if (json.get("success").getAsBoolean()) { + com.google.gson.JsonObject data = json.getAsJsonObject("data"); + String mcUuid = data.get("mc_uuid").getAsString(); + String mcUsername = data.get("mc_username").getAsString(); + + plugin.getLogger().info("[正版验证] 粘贴码验证成功: " + playerName + " -> " + mcUsername + " (" + mcUuid + ")"); + + // 记录为已验证正版玩家 + plugin.addVerifiedPremiumPlayer(playerName, mcUuid, mcUsername); + + // 清理会话 + minecraftAuthSessions.remove(playerName); + BukkitRunnable poller = minecraftAuthPollers.remove(playerName); + if (poller != null) poller.cancel(); + + // 在主线程执行自动登录 + Bukkit.getScheduler().runTask(plugin, () -> { + if (player.isOnline() && !plugin.getLoggedIn().contains(playerName)) { + player.sendMessage("§a§l正版验证成功!欢迎 " + mcUsername + "!"); + plugin.autoLogin(player, "premium"); + } + }); + } else { + String error = json.has("error") ? json.get("error").getAsString() : "验证失败"; + player.sendMessage("§c" + error); + } + } catch (Exception e) { + plugin.getLogger().warning("[正版验证] 粘贴码验证异常: " + e.getMessage()); + player.sendMessage("§c验证请求失败,请稍后重试"); + } + }); + } + /** * 同步在线玩家列表到PHP端(用于Web登录状态检查) * 注意:推送所有在线玩家(包括未登录的),PHP端通过 web_login_verified 判断是否已认证 diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 819fdd9..e5202b9 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -123,6 +123,9 @@ commands: 正版: description: 正版登录(中文版) usage: /正版 + mscode: + description: 粘贴Microsoft OAuth授权码 + usage: /mscode <授权码> tpa: description: tp传送指令 usage: /tpa