diff --git a/Authentication/MicrosoftAuthentication.cs b/Authentication/MicrosoftAuthentication.cs index 945d28b..2cf28fe 100644 --- a/Authentication/MicrosoftAuthentication.cs +++ b/Authentication/MicrosoftAuthentication.cs @@ -104,6 +104,33 @@ public async ValueTask GetTokenResponse(RetrieveDeviceCode dev ClientId = deviceCodeInfo.ClientId }; } + else + { + // 先判断是否正在等待中,直接使用 Contains,避免额外解析开销 + if (!tokenJson.Contains("authorization_pending")) + { + try + { + var errorData = tokenJson.ToJsonEntry(); + switch (errorData.Error) + { + case "authorization_declined": + throw new Exception("用户拒绝了授权"); + case "bad_verification_code": + throw new Exception("提交的 device_code 无效"); + case "expired_token": + throw new Exception("登录已超时,请重试"); // 避免轮询时正好过期导致抓不到异常 + default: + throw new Exception(errorData.Error); + // 实际上还有其他的错误类型,但按理说不会出现,比如 slow_down + } + } + catch (Exception e) + { + throw new Exception("轮询获取 Token 错误:" + e.Message); + } + } + } await Task.Delay(pollingInterval); } @@ -124,7 +151,7 @@ public async ValueTask MicrosoftAuthAsync(GetTokenResponse tok string? refreshToken = null) { if (refreshToken == null) return await GetMicrosoftAuthInfo(tokenInfo, action); - + // 刷新令牌 action("正在刷新令牌"); @@ -270,7 +297,7 @@ public async ValueTask GetMicrosoftAuthInfo(GetTokenResponse t var minecraftProfile = profileContent.ToJsonEntry(); if (minecraftProfile == null) throw new Exception("验证失败"); - + var uuid = minecraftProfile.Uuid; var name = minecraftProfile.Name; var skinUrl = minecraftProfile.Skins[0].Url; diff --git a/Models/Authentication/GetTokenErrorResponse.cs b/Models/Authentication/GetTokenErrorResponse.cs new file mode 100644 index 0000000..ded04c3 --- /dev/null +++ b/Models/Authentication/GetTokenErrorResponse.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; + +namespace StarLight_Core.Models.Authentication; + +/// +/// 令牌错误信息 +/// +/// 查看文档 +public class GetTokenErrorResponse +{ + /// + /// 错误 + /// + [JsonPropertyName("error")] + public string Error { get; set; } +} \ No newline at end of file diff --git a/Skin/Fetchers/MicrosoftSkinFetcher.cs b/Skin/Fetchers/MicrosoftSkinFetcher.cs index bbd068b..f942f5a 100644 --- a/Skin/Fetchers/MicrosoftSkinFetcher.cs +++ b/Skin/Fetchers/MicrosoftSkinFetcher.cs @@ -1,4 +1,5 @@ using System.Text; +using StarLight_Core.Models.Authentication; using StarLight_Core.Models.Skin; using StarLight_Core.Utilities; @@ -9,6 +10,16 @@ namespace StarLight_Core.Skin.Fetchers; /// public class MicrosoftSkinFetcher { + /// + /// 获取微软皮肤 + /// + /// 皮肤图片字节信息 + /// 皮肤图片字节信息 + public static async Task GetMicrosoftSkinAsync(MicrosoftAccount account) + { + return await GetMicrosoftSkinAsync(account.Uuid); + } + /// /// 获取微软皮肤 /// @@ -17,6 +28,7 @@ public class MicrosoftSkinFetcher public static async Task GetMicrosoftSkinAsync(string uuid) { const string baseUrl = "https://sessionserver.mojang.com/session/minecraft/profile/"; + uuid = uuid.Replace("-", ""); var skinJson = await HttpUtil.GetStringAsync(baseUrl + uuid); var skinUrl = Encoding.UTF8.GetString( diff --git a/Skin/Fetchers/YggdrasilSkinFetcher.cs b/Skin/Fetchers/YggdrasilSkinFetcher.cs new file mode 100644 index 0000000..5d622e2 --- /dev/null +++ b/Skin/Fetchers/YggdrasilSkinFetcher.cs @@ -0,0 +1,45 @@ +using StarLight_Core.Models.Authentication; +using StarLight_Core.Models.Skin; +using StarLight_Core.Utilities; +using System.Security.Cryptography.X509Certificates; +using System.Security.Principal; +using System.Text; + +namespace StarLight_Core.Skin.Fetchers; + +/// +/// 外置皮肤获取器 +/// +public class YggdrasilSkinFetcher +{ + /// + /// 获取外置皮肤 + /// + /// 外置账户 + /// 皮肤图片字节信息 + public static async Task GetYggdrasilSkinAsync(YggdrasilAccount account) + { + return await GetYggdrasilSkinAsync(account.ServerUrl, account.Uuid); + } + + /// + /// 获取外置皮肤 + /// + /// 服务器 Url + /// 外置账户 Uuid + /// 皮肤图片字节信息 + public static async Task GetYggdrasilSkinAsync(string serverUrl, string uuid) + { + var baseUrl = serverUrl.TrimEnd("/"); + uuid = uuid.Replace("-", ""); + var skinJson = await HttpUtil.GetStringAsync($"{baseUrl}/sessionserver/session/minecraft/profile/{uuid}"); + var skinUrl = + Encoding.UTF8.GetString( + Convert.FromBase64String( + skinJson.ToJsonEntry().Properties.First().Value)) + .ToJsonEntry() + .Textures.Skin.Url; + using var httpClient = new HttpClient(); + return await httpClient.GetByteArrayAsync(skinUrl); + } +} \ No newline at end of file