From 72ca0f20a129ad87747875f35a4cc6c2bfd7f7f7 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Sat, 4 Sep 2021 02:07:58 +0800 Subject: [PATCH] feat: MicrosoftAccount: find skin by uuid instead of fetching profile. --- .../hmcl/auth/microsoft/MicrosoftAccount.java | 17 ++++-- .../hmcl/auth/microsoft/MicrosoftService.java | 53 +++++++++++++++---- 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftAccount.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftAccount.java index aae5c2062..227b032bc 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftAccount.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftAccount.java @@ -21,14 +21,17 @@ import javafx.beans.binding.ObjectBinding; import org.jackhuang.hmcl.auth.*; import org.jackhuang.hmcl.auth.yggdrasil.Texture; import org.jackhuang.hmcl.auth.yggdrasil.TextureType; +import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilService; import org.jackhuang.hmcl.util.javafx.BindingMapping; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.UUID; +import java.util.logging.Level; import static java.util.Objects.requireNonNull; +import static org.jackhuang.hmcl.util.Logging.LOG; public class MicrosoftAccount extends Account { @@ -77,7 +80,7 @@ public class MicrosoftAccount extends Account { @Override public AuthInfo logIn() throws AuthenticationException { if (!authenticated) { - if (service.validate(session.getTokenType(), session.getAccessToken())) { + if (service.validate(session.getNotAfter(), session.getTokenType(), session.getAccessToken())) { authenticated = true; } else { MicrosoftSession acquiredSession = service.refresh(session); @@ -116,9 +119,15 @@ public class MicrosoftAccount extends Account { @Override public ObjectBinding>> getTextures() { - return BindingMapping.of(service.getProfileRepository().binding(session.getAuthorization())) - .map(profile -> profile.flatMap(MicrosoftService::getTextures)); - + return BindingMapping.of(service.getProfileRepository().binding(getUUID())) + .map(profile -> profile.flatMap(it -> { + try { + return YggdrasilService.getTextures(it); + } catch (ServerResponseMalformedException e) { + LOG.log(Level.WARNING, "Failed to parse texture payload", e); + return Optional.empty(); + } + })); } @Override diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java index a322446a8..1af3d4a40 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java @@ -17,15 +17,16 @@ */ package org.jackhuang.hmcl.auth.microsoft; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.JsonParseException; import com.google.gson.annotations.SerializedName; import org.jackhuang.hmcl.auth.*; +import org.jackhuang.hmcl.auth.yggdrasil.CompleteGameProfile; import org.jackhuang.hmcl.auth.yggdrasil.RemoteAuthenticationException; import org.jackhuang.hmcl.auth.yggdrasil.Texture; import org.jackhuang.hmcl.auth.yggdrasil.TextureType; -import org.jackhuang.hmcl.util.gson.JsonUtils; -import org.jackhuang.hmcl.util.gson.TolerableValidationException; -import org.jackhuang.hmcl.util.gson.Validation; +import org.jackhuang.hmcl.util.gson.*; import org.jackhuang.hmcl.util.io.HttpRequest; import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jackhuang.hmcl.util.io.ResponseCodeException; @@ -60,17 +61,17 @@ public class MicrosoftService { private final OAuthCallback callback; - private final ObservableOptionalCache profileRepository; + private final ObservableOptionalCache profileRepository; public MicrosoftService(OAuthCallback callback) { this.callback = requireNonNull(callback); - this.profileRepository = new ObservableOptionalCache<>(authorization -> { - LOG.info("Fetching properties"); - return getCompleteProfile(authorization); + this.profileRepository = new ObservableOptionalCache<>(uuid -> { + LOG.info("Fetching properties of " + uuid); + return getCompleteGameProfile(uuid); }, (uuid, e) -> LOG.log(Level.WARNING, "Failed to fetch properties of " + uuid, e), POOL); } - public ObservableOptionalCache getProfileRepository() { + public ObservableOptionalCache getProfileRepository() { return profileRepository; } @@ -213,10 +214,14 @@ public class MicrosoftService { } } - public boolean validate(String tokenType, String accessToken) throws AuthenticationException { + public boolean validate(long notAfter, String tokenType, String accessToken) throws AuthenticationException { requireNonNull(tokenType); requireNonNull(accessToken); + if (System.currentTimeMillis() > notAfter) { + return false; + } + try { getMinecraftProfile(tokenType, accessToken); return true; @@ -275,6 +280,31 @@ public class MicrosoftService { return JsonUtils.fromNonNullJson(result, MinecraftProfileResponse.class); } + public Optional getCompleteGameProfile(UUID uuid) throws AuthenticationException { + Objects.requireNonNull(uuid); + + return Optional.ofNullable(GSON.fromJson(request(NetworkUtils.toURL("https://sessionserver.mojang.com/session/minecraft/profile/" + UUIDTypeAdapter.fromUUID(uuid)), null), CompleteGameProfile.class)); + } + + private static String request(URL url, Object payload) throws AuthenticationException { + try { + if (payload == null) + return NetworkUtils.doGet(url); + else + return NetworkUtils.doPost(url, payload instanceof String ? (String) payload : GSON.toJson(payload), "application/json"); + } catch (IOException e) { + throw new ServerDisconnectException(e); + } + } + + private static T fromJson(String text, Class typeOfT) throws ServerResponseMalformedException { + try { + return GSON.fromJson(text, typeOfT); + } catch (JsonParseException e) { + throw new ServerResponseMalformedException(text, e); + } + } + public static class XboxAuthorizationException extends AuthenticationException { private final long errorCode; @@ -487,4 +517,9 @@ public class MicrosoftService { String waitFor() throws InterruptedException, ExecutionException; } + private static final Gson GSON = new GsonBuilder() + .registerTypeAdapter(UUID.class, UUIDTypeAdapter.INSTANCE) + .registerTypeAdapterFactory(ValidationTypeAdapterFactory.INSTANCE) + .create(); + }