You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java实现Microsoft客户端登录获取Token及Minecraft游戏验证求助

Java实现Microsoft登录获取Minecraft Token及游戏拥有验证

以下是完整的实现流程和代码示例,覆盖从Microsoft OAuth2授权到Minecraft Token获取、游戏权限验证的全流程:

核心流程概述

  1. 使用PKCE模式完成Microsoft OAuth2授权,获取授权码
  2. 通过授权码交换得到Microsoft access_token
  3. 用Microsoft token获取Xbox Live身份凭证
  4. 通过Xbox凭证获取XSTS令牌
  5. 用XSTS令牌兑换Minecraft服务的access_token
  6. 调用Minecraft官方API验证账号是否拥有游戏权限

1. 生成PKCE参数(安全授权必备)

PKCE用于桌面应用的安全授权,避免授权码被拦截:

import java.security.SecureRandom;
import java.util.Base64;

public class PKCEGenerator {
    public static String generateCodeVerifier() {
        SecureRandom random = new SecureRandom();
        byte[] bytes = new byte[32];
        random.nextBytes(bytes);
        return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
    }

    public static String generateCodeChallenge(String codeVerifier) {
        try {
            java.security.MessageDigest md = java.security.MessageDigest.getInstance("SHA-256");
            byte[] digest = md.digest(codeVerifier.getBytes("UTF-8"));
            return Base64.getUrlEncoder().withoutPadding().encodeToString(digest);
        } catch (Exception e) {
            throw new RuntimeException("生成Code Challenge失败", e);
        }
    }
}

2. 构造Microsoft授权URL

引导用户在浏览器完成登录,获取授权码:

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

public class AuthUrlBuilder {
    // Minecraft官方启动器使用的公开客户端ID
    private static final String CLIENT_ID = "00000000402b5328";
    private static final String REDIRECT_URI = "https://login.live.com/oauth20_desktop.srf";
    private static final String SCOPE = "XboxLive.signin offline_access";

    public static String buildAuthUrl(String codeChallenge) {
        return String.format(
            "https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize?client_id=%s&response_type=code&redirect_uri=%s&scope=%s&code_challenge=%s&code_challenge_method=S256",
            CLIENT_ID,
            URLEncoder.encode(REDIRECT_URI, StandardCharsets.UTF_8),
            URLEncoder.encode(SCOPE, StandardCharsets.UTF_8),
            codeChallenge
        );
    }
}

3. 交换授权码获取Microsoft Token

用用户返回的授权码,结合code_verifier获取Microsoft的access_token和refresh_token:

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Map;
import com.google.gson.Gson;

public class MicrosoftTokenFetcher {
    private static final String CLIENT_ID = "00000000402b5328";
    private static final String REDIRECT_URI = "https://login.live.com/oauth20_desktop.srf";
    private static final String TOKEN_URL = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token";

    public static Map<String, Object> getMicrosoftToken(String code, String codeVerifier) throws Exception {
        HttpClient client = HttpClient.newHttpClient();
        String body = String.format(
            "client_id=%s&code=%s&redirect_uri=%s&grant_type=authorization_code&code_verifier=%s",
            CLIENT_ID,
            code,
            URLEncoder.encode(REDIRECT_URI, StandardCharsets.UTF_8),
            codeVerifier
        );

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(TOKEN_URL))
            .header("Content-Type", "application/x-www-form-urlencoded")
            .POST(HttpRequest.BodyPublishers.ofString(body))
            .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        return new Gson().fromJson(response.body(), Map.class);
    }
}

4. 获取Xbox Live身份令牌

将Microsoft token转换为Xbox Live的身份凭证:

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Map;
import com.google.gson.Gson;

public class XboxLiveTokenFetcher {
    private static final String XBL_AUTH_URL = "https://user.auth.xboxlive.com/user/authenticate";

    public static Map<String, Object> getXboxLiveToken(String microsoftAccessToken) throws Exception {
        HttpClient client = HttpClient.newHttpClient();
        String payload = new Gson().toJson(Map.of(
            "Properties", Map.of(
                "AuthMethod", "RPS",
                "SiteName", "user.auth.xboxlive.com",
                "RpsTicket", "d=" + microsoftAccessToken
            ),
            "RelyingParty", "http://auth.xboxlive.com",
            "TokenType", "JWT"
        ));

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(XBL_AUTH_URL))
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(payload))
            .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        return new Gson().fromJson(response.body(), Map.class);
    }
}

5. 获取XSTS令牌

通过Xbox Live令牌获取XSTS凭证,用于访问Minecraft服务:

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Map;
import com.google.gson.Gson;

public class XstsTokenFetcher {
    private static final String XSTS_AUTH_URL = "https://xsts.auth.xboxlive.com/xsts/authorize";

    public static Map<String, Object> getXstsToken(String xboxLiveToken) throws Exception {
        HttpClient client = HttpClient.newHttpClient();
        String payload = new Gson().toJson(Map.of(
            "Properties", Map.of(
                "SandboxId", "RETAIL",
                "UserTokens", new String[]{xboxLiveToken}
            ),
            "RelyingParty", "rp://api.minecraftservices.com/",
            "TokenType", "JWT"
        ));

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(XSTS_AUTH_URL))
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(payload))
            .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        return new Gson().fromJson(response.body(), Map.class);
    }
}

6. 获取Minecraft Token并验证游戏权限

最后兑换Minecraft服务的token,并验证账号是否拥有Java版游戏:

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.List;
import java.util.Map;
import com.google.gson.Gson;

public class MinecraftAuth {
    private static final String MINECRAFT_TOKEN_URL = "https://api.minecraftservices.com/authentication/login_with_xbox";
    private static final String MINECRAFT_OWNERSHIP_URL = "https://api.minecraftservices.com/entitlements/mcstore";
    private static final String MINECRAFT_PROFILE_URL = "https://api.minecraftservices.com/minecraft/profile";

    public static Map<String, Object> getMinecraftToken(String xstsToken, String uhs) throws Exception {
        HttpClient client = HttpClient.newHttpClient();
        String payload = new Gson().toJson(Map.of(
            "identityToken", "XBL3.0 x=" + uhs + ";" + xstsToken
        ));

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(MINECRAFT_TOKEN_URL))
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(payload))
            .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        return new Gson().fromJson(response.body(), Map.class);
    }

    public static boolean hasMinecraftJavaEdition(String minecraftAccessToken) throws Exception {
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(MINECRAFT_OWNERSHIP_URL))
            .header("Authorization", "Bearer " + minecraftAccessToken)
            .GET()
            .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        Map<String, Object> entitlements = new Gson().fromJson(response.body(), Map.class);
        List<Map<String, Object>> items = (List<Map<String, Object>>) entitlements.get("items");
        
        for (Map<String, Object> item : items) {
            if ("game_minecraft".equals(item.get("name"))) {
                return true;
            }
        }
        return false;
    }

    public static Map<String, Object> getMinecraftProfile(String minecraftAccessToken) throws Exception {
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(MINECRAFT_PROFILE_URL))
            .header("Authorization", "Bearer " + minecraftAccessToken)
            .GET()
            .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        return new Gson().fromJson(response.body(), Map.class);
    }
}

完整流程调用示例

import java.util.Map;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) throws Exception {
        // 1. 生成PKCE参数
        String codeVerifier = PKCEGenerator.generateCodeVerifier();
        String codeChallenge = PKCEGenerator.generateCodeChallenge(codeVerifier);

        // 2. 输出授权URL,引导用户登录
        String authUrl = AuthUrlBuilder.buildAuthUrl(codeChallenge);
        System.out.println("请在浏览器打开以下链接登录Microsoft账号:");
        System.out.println(authUrl);
        System.out.println("登录完成后,复制URL中的`code`参数并输入:");
        
        Scanner scanner = new Scanner(System.in);
        String authCode = scanner.nextLine().trim();

        // 3. 获取Microsoft Token
        Map<String, Object> microsoftToken = MicrosoftTokenFetcher.getMicrosoftToken(authCode, codeVerifier);
        String msAccessToken = (String) microsoftToken.get("access_token");

        // 4. 获取Xbox Live Token
        Map<String, Object> xblToken = XboxLiveTokenFetcher.getXboxLiveToken(msAccessToken);
        String xblTokenValue = (String) xblToken.get("Token");
        // 解析UHS(用户唯一标识)
        String uhs = ((Map<String, Object>) ((List<?>) ((Map<String, Object>) xblToken.get("DisplayClaims")).get("xui")).get(0)).get("uhs").toString();

        // 5. 获取XSTS Token
        Map<String, Object> xstsToken = XstsTokenFetcher.getXstsToken(xblTokenValue);
        String xstsTokenValue = (String) xstsToken.get("Token");

        // 6. 获取Minecraft Token
        Map<String, Object> mcToken = MinecraftAuth.getMinecraftToken(xstsTokenValue, uhs);
        String mcAccessToken = (String) mcToken.get("access_token");
        System.out.println("\nMinecraft Access Token: " + mcAccessToken);

        // 7. 验证游戏拥有状态
        boolean hasJavaEdition = MinecraftAuth.hasMinecraftJavaEdition(mcAccessToken);
        System.out.println("是否拥有Minecraft Java版:" + (hasJavaEdition ? "是" : "否"));

        // 8. 获取玩家信息
        Map<String, Object> profile = MinecraftAuth.getMinecraftProfile(mcAccessToken);
        System.out.println("玩家ID:" + profile.get("id"));
        System.out.println("玩家名称:" + profile.get("name"));
    }
}

注意事项

  • 依赖引入:需要添加Gson库处理JSON解析(Maven依赖:com.google.code.gson:gson:2.10.1)
  • 异常处理:实际应用中需完善网络异常、授权失败等场景的捕获与处理
  • Token刷新:当Minecraft access_token过期时,可使用Microsoft的refresh_token重新获取,流程类似步骤3,只需将grant_type改为refresh_token
  • 权限范围:确保授权时的scope包含XboxLive.signin和offline_access,前者用于Xbox授权,后者用于获取refresh_token实现持久登录

内容的提问来源于stack exchange,提问作者secRaphyTwin Go

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 03:44:57