添加/mscode命令:支持粘贴Microsoft OAuth授权码(公共客户端流程)
This commit is contained in:
@@ -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)) {
|
||||
|
||||
@@ -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 判断是否已认证
|
||||
|
||||
@@ -123,6 +123,9 @@ commands:
|
||||
正版:
|
||||
description: 正版登录(中文版)
|
||||
usage: /正版
|
||||
mscode:
|
||||
description: 粘贴Microsoft OAuth授权码
|
||||
usage: /mscode <授权码>
|
||||
tpa:
|
||||
description: tp传送指令
|
||||
usage: /tpa
|
||||
|
||||
Reference in New Issue
Block a user