修复正版验证三大问题:1)paste_code_page Content-Type改为text/html修复HTML源码输出 2)/mscode支持粘贴完整URL自动提取code参数 3)HTTP错误处理改进,非200也返回body显示PHP错误信息 4)verify_code改为JSON格式POST

This commit is contained in:
2026-07-17 16:13:40 +08:00
parent c3d2f81e9e
commit f83458fd4b
2 changed files with 82 additions and 23 deletions
+41 -6
View File
@@ -13,6 +13,7 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import java.nio.charset.StandardCharsets;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityDeathEvent;
@@ -1602,6 +1603,37 @@ public class Main extends JavaPlugin
// ===== Microsoft OAuth正版验证 =====
/**
* 从用户输入中提取Microsoft OAuth授权码
* 支持:纯授权码、完整URL(含code=参数)
*/
private String extractCodeFromInput(String input) {
if (input == null || input.isEmpty()) return null;
input = input.trim();
// 如果包含code=参数,从中提取
if (input.contains("code=") || input.startsWith("http")) {
try {
// 尝试解析URL中的query参数
int qIdx = input.indexOf('?');
if (qIdx >= 0) {
String query = input.substring(qIdx + 1);
for (String param : query.split("&")) {
String[] kv = param.split("=", 2);
if (kv.length == 2 && kv[0].equals("code")) {
return java.net.URLDecoder.decode(kv[1], StandardCharsets.UTF_8);
}
}
}
} catch (Exception e) {
// 解析失败,继续尝试其他方式
}
}
// 直接作为授权码返回
return input;
}
/**
* 处理正版验证命令:调用PHP后端获取授权URL,显示给玩家,启动轮询
*/
@@ -5998,7 +6030,7 @@ public class Main extends JavaPlugin
return true;
}
// ===== /mscode <授权码>:粘贴Microsoft OAuth授权码 =====
// ===== /mscode <授权码或URL>:粘贴Microsoft OAuth授权码或完整URL =====
if (cmdName.equals("mscode")) {
if (!(sender instanceof Player)) {
sender.sendMessage("§c仅玩家可用");
@@ -6010,13 +6042,16 @@ public class Main extends JavaPlugin
return true;
}
if (args.length < 1) {
p.sendMessage("§c用法: /mscode <授权码>");
p.sendMessage("§7从Microsoft登录页面复制授权码后粘贴到这里");
p.sendMessage("§c用法: /mscode <授权码或完整URL>");
p.sendMessage("§7粘贴Microsoft登录后的授权码,或粘贴包含code=的完整URL");
return true;
}
String code = args[0].trim();
if (code.length() < 10) {
p.sendMessage("§c授权码格式不正确,请复制完整的授权码");
String rawInput = String.join(" ", args).trim();
// 自动从URL中提取code参数
String code = extractCodeFromInput(rawInput);
if (code == null || code.length() < 10) {
p.sendMessage("§c无法从输入中提取有效的授权码");
p.sendMessage("§7请粘贴授权码或包含code=的完整URL");
return true;
}
// 检查是否有进行中的会话
+41 -17
View File
@@ -555,11 +555,15 @@ public class WebManager {
detectPhpBusy(body); // ★ 锁库检测
return body;
}
// ★ 500时记录响应体(PHP错误信息在body里)
// ★ 4xx/5xx也返回body(PHP的error()返回有用的JSON错误信息)
String shortUrl = urlStr.length() > 120 ? urlStr.substring(0, 120) + "..." : urlStr;
String body = resp.body();
String shortBody = (body != null && body.length() > 200) ? body.substring(0, 200) : body;
plugin.getLogger().warning("[Web通信] GET HTTP " + resp.statusCode() + ": " + shortUrl + " | 响应: " + shortBody);
// 返回body让调用方可以解析PHP错误信息
if (body != null && !body.isEmpty()) {
return body;
}
return null;
} catch (Exception e) {
// ★ SSL断路器:跟踪连续失败(所有连接异常都计数)
@@ -633,11 +637,14 @@ public class WebManager {
if (resp.statusCode() >= 200 && resp.statusCode() < 300) {
return resp.body();
}
// ★ 非2xx记录响应体
// ★ 非2xx也返回body(PHP的error()返回有用的JSON错误信息)
String shortUrl = urlStr.length() > 120 ? urlStr.substring(0, 120) + "..." : urlStr;
String body = resp.body();
String shortBody = (body != null && body.length() > 200) ? body.substring(0, 200) : body;
plugin.getLogger().warning("[Web通信] POST HTTP " + resp.statusCode() + ": " + shortUrl + " | 响应: " + shortBody);
if (body != null && !body.isEmpty()) {
return body;
}
return null;
} catch (Exception e) {
// ★ SSL断路器:跟踪连续失败(所有连接异常都计数)
@@ -674,11 +681,14 @@ public class WebManager {
sslDowngraded = false; // 恢复正常HTTPS
return resp.body();
}
// ★ 非2xx记录响应体
// ★ 非2xx也返回body(PHP的error()返回有用的JSON错误信息)
String shortUrl = urlStr.length() > 120 ? urlStr.substring(0, 120) + "..." : urlStr;
String body = resp.body();
String shortBody = (body != null && body.length() > 200) ? body.substring(0, 200) : body;
plugin.getLogger().warning("[Web通信] POST HTTP " + resp.statusCode() + ": " + shortUrl + " | 响应: " + shortBody);
if (body != null && !body.isEmpty()) {
return body;
}
return null;
} catch (Exception e) {
// ★ SSL断路器:跟踪连续失败(所有连接异常都计数)
@@ -4945,7 +4955,16 @@ public class WebManager {
return;
}
com.google.gson.JsonObject json = com.google.gson.JsonParser.parseString(response).getAsJsonObject();
com.google.gson.JsonObject json;
try {
json = com.google.gson.JsonParser.parseString(response).getAsJsonObject();
} catch (Exception parseEx) {
// PHP返回了非JSON(如500错误页面),显示截断内容
String preview = response.length() > 150 ? response.substring(0, 150) + "..." : response;
player.sendMessage("§c后端返回异常: " + preview);
plugin.getLogger().warning("[正版验证] PHP返回非JSON: " + preview);
return;
}
if (!json.get("success").getAsBoolean()) {
String error = json.has("error") ? json.get("error").getAsString() : "未知错误";
player.sendMessage("§c验证失败: " + error);
@@ -4965,13 +4984,9 @@ public class WebManager {
Bukkit.getScheduler().runTask(plugin, () -> {
if (player.isOnline()) {
player.sendMessage("§6§l===== 正版验证 =====");
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方式一: 点击下方链接打开粘贴页面");
player.sendMessage("§7方式二: /mscode <粘贴完整URL或授权码>");
player.sendMessage("§7步骤: 打开链接 → 登录 → 复制地址栏URL → 粘贴");
player.sendMessage("§7链接10分钟内有效");
player.sendMessage("§6§l====================");
// 尝试发送可点击的URL(授权链接)
@@ -5153,18 +5168,27 @@ public class WebManager {
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;
// POST参数(JSON格式,与doPost的Content-Type一致)
com.google.gson.JsonObject postData = new com.google.gson.JsonObject();
postData.addProperty("session_id", sessionId);
postData.addProperty("code", code);
postData.addProperty("secret", secret);
String response = doPost(url, postData);
String response = doPost(url, postData.toString());
if (response == null) {
player.sendMessage("§c无法连接Web后端");
return;
}
com.google.gson.JsonObject json = com.google.gson.JsonParser.parseString(response).getAsJsonObject();
com.google.gson.JsonObject json;
try {
json = com.google.gson.JsonParser.parseString(response).getAsJsonObject();
} catch (Exception parseEx) {
String preview = response.length() > 150 ? response.substring(0, 150) + "..." : response;
player.sendMessage("§c后端返回异常: " + preview);
plugin.getLogger().warning("[正版验证] verify_code返回非JSON: " + preview);
return;
}
if (json.get("success").getAsBoolean()) {
com.google.gson.JsonObject data = json.getAsJsonObject("data");
String mcUuid = data.get("mc_uuid").getAsString();