添加/mscode命令:支持粘贴Microsoft OAuth授权码(公共客户端流程)

This commit is contained in:
2026-07-17 14:02:08 +08:00
parent f8b44dd97b
commit c3d2f81e9e
3 changed files with 125 additions and 6 deletions
+33
View File
@@ -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)) {
+89 -6
View File
@@ -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 判断是否已认证
+3
View File
@@ -123,6 +123,9 @@ commands:
正版:
description: 正版登录(中文版)
usage: /正版
mscode:
description: 粘贴Microsoft OAuth授权码
usage: /mscode <授权码>
tpa:
description: tp传送指令
usage: /tpa