From e29e12c9fed0787790f16d7f52d958c322b38ecd Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Sun, 13 Nov 2022 21:40:51 +0800 Subject: [PATCH] support login api for #207 --- .../Snap.Hutao/Core/CoreEnvironment.cs | 19 +++++++ .../Snap.Hutao/Web/Hoyolab/ApiEndpoints.cs | 6 +++ .../DynamicSecret/DynamicSecretProvider.cs | 2 +- .../DynamicSecret/DynamicSecretProvider2.cs | 16 ++++-- .../Web/Hoyolab/DynamicSecret/SaltType.cs | 5 ++ .../Web/Hoyolab/Passport/PassportClient.cs | 1 + .../Web/Hoyolab/Passport/PassportClient2.cs | 53 +++++++++++++++++++ .../Web/Hoyolab/Passport/UidCookieToken.cs | 22 ++++++++ 8 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient2.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/UidCookieToken.cs diff --git a/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs b/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs index d7068aae..6f4ed18c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs @@ -4,6 +4,8 @@ using Microsoft.Win32; using Snap.Hutao.Core.Convert; using Snap.Hutao.Extension; +using Snap.Hutao.Web.Hoyolab.DynamicSecret; +using System.Collections.Immutable; using System.Text.Encodings.Web; using Windows.ApplicationModel; @@ -36,6 +38,11 @@ internal static class CoreEnvironment /// public const string DynamicSecretX6Salt = "t0qEgfub6cvueAPgR5m9aQWWVciEer7v"; + /// + /// LoginApi的盐 + /// + public const string DynamicSecretPRODSalt = "JwYDpKvLj6MrMqqYU6jTKF17KNO2PXoS"; + /// /// 米游社请求UA /// @@ -46,6 +53,18 @@ internal static class CoreEnvironment /// public const string HoyolabXrpcVersion = "2.40.1"; + /// + /// 动态密钥 + /// + public static readonly ImmutableDictionary DynamicSecrets = new Dictionary() + { + [SaltType.K2] = "fdv0fY9My9eA7MR0NpjGP9RjueFvjUSQ", + [SaltType.LK2] = "jEpJb9rRARU2rXDA9qYbZ3selxkuct9a", + [SaltType.X4] = "xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs", + [SaltType.X6] = "t0qEgfub6cvueAPgR5m9aQWWVciEer7v", + [SaltType.PROD] = "JwYDpKvLj6MrMqqYU6jTKF17KNO2PXoS", + }.ToImmutableDictionary(); + /// /// 标准UA /// diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/ApiEndpoints.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/ApiEndpoints.cs index 8ade2c73..d4aba5ca 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/ApiEndpoints.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/ApiEndpoints.cs @@ -181,6 +181,11 @@ internal static class ApiEndpoints #region ma-cn-session + /// + /// 获取 CookieToken + /// + public const string AccountCookieAccountInfoBySToken = $"{PassportApi}/account/auth/api/getCookieAccountInfoBySToken"; + /// /// 验证 Ltoken 有效性 /// @@ -213,6 +218,7 @@ internal static class ApiEndpoints private const string Hk4eApiAnnouncementApi = $"{Hk4eApi}/common/hk4e_cn/announcement/api"; private const string Hk4eApiGachaInfoApi = $"{Hk4eApi}/event/gacha_info/api"; + private const string PassportApi = "passport-api.mihoyo.com"; private const string PassportApiV4 = "passport-api-v4.mihoyo.com"; private const string SdkStatic = "https://sdk-static.mihoyo.com"; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretProvider.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretProvider.cs index 2d0bc6a6..ba42f8fa 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretProvider.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretProvider.cs @@ -27,7 +27,7 @@ internal abstract class DynamicSecretProvider : Md5Convert string r = GetRandomString(); - string salt = saltType == SaltType.K2 ? Core.CoreEnvironment.DynamicSecretK2Salt : Core.CoreEnvironment.DynamicSecretLK2Salt; + string salt = Core.CoreEnvironment.DynamicSecrets[saltType]; string check = ToHexString($"salt={salt}&t={t}&r={r}").ToLowerInvariant(); return $"{t},{r},{check}"; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretProvider2.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretProvider2.cs index 0a1d3ed2..fbd423cb 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretProvider2.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretProvider2.cs @@ -20,7 +20,7 @@ internal abstract class DynamicSecretProvider2 : Md5Convert /// 密钥 public static string Create(SaltType saltType, JsonSerializerOptions options, string queryUrl, object? postBody = null) { - Verify.Operation(saltType is SaltType.X6 or SaltType.X4, "SALT 值无效"); + Verify.Operation(saltType is SaltType.X6 or SaltType.X4 or SaltType.PROD, "SALT 值无效"); // unix timestamp long t = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); @@ -29,19 +29,29 @@ internal abstract class DynamicSecretProvider2 : Md5Convert int r = GetRandom(); // body - string b = postBody is null ? string.Empty : JsonSerializer.Serialize(postBody, options); + string b = postBody is null ? GetDefaultBody(saltType) : JsonSerializer.Serialize(postBody, options); // query string[] queries = queryUrl.Split('?', 2); string q = queries.Length == 2 ? string.Join('&', queries[1].Split('&').OrderBy(x => x)) : string.Empty; // check - string salt = saltType == SaltType.X6 ? Core.CoreEnvironment.DynamicSecretX6Salt : Core.CoreEnvironment.DynamicSecretX4Salt; + string salt = Core.CoreEnvironment.DynamicSecrets[saltType]; string check = ToHexString($"salt={salt}&t={t}&r={r}&b={b}&q={q}").ToLowerInvariant(); return $"{t},{r},{check}"; } + private static string GetDefaultBody(SaltType saltType) + { + return saltType switch + { + SaltType.X4 or SaltType.X6 => string.Empty, + SaltType.PROD => "{}", + _ => throw Must.NeverHappen(((int)saltType).ToString()), + }; + } + private static int GetRandom() { // 原汁原味 diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/SaltType.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/SaltType.cs index a8582f31..844d97f4 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/SaltType.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/SaltType.cs @@ -23,6 +23,11 @@ public enum SaltType /// X6, + /// + /// PROD + /// + PROD, + /// /// K2 /// diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient.cs index 781bd814..e0930f32 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient.cs @@ -4,6 +4,7 @@ using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; using Snap.Hutao.Model.Entity; using Snap.Hutao.Web.Hoyolab.Annotation; +using Snap.Hutao.Web.Hoyolab.DynamicSecret; using Snap.Hutao.Web.Response; using System.Net.Http; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient2.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient2.cs new file mode 100644 index 00000000..0d059ef4 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient2.cs @@ -0,0 +1,53 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; +using Snap.Hutao.Model.Entity; +using Snap.Hutao.Web.Hoyolab.Annotation; +using Snap.Hutao.Web.Hoyolab.DynamicSecret; +using Snap.Hutao.Web.Response; +using System.Net.Http; + +namespace Snap.Hutao.Web.Hoyolab.Passport; + +/// +/// 通行证客户端 XRPC 版 +/// +[HttpClient(HttpClientConfigration.XRpc)] +internal class PassportClient2 +{ + private readonly HttpClient httpClient; + private readonly JsonSerializerOptions options; + private readonly ILogger logger; + + /// + /// 构造一个新的通行证客户端 + /// + /// http客户端 + /// json序列化选项 + /// 日志器 + public PassportClient2(HttpClient httpClient, JsonSerializerOptions options, ILogger logger) + { + this.httpClient = httpClient; + this.options = options; + this.logger = logger; + } + + /// + /// 异步获取 CookieToken + /// + /// 用户 + /// 取消令牌 + /// uid 与 cookie token + [ApiInformation(Cookie = CookieType.Stoken, Salt = SaltType.PROD)] + public async Task GetCookieAccountInfoBySTokenAsync(User user, CancellationToken token) + { + Response? resp = await httpClient + .SetUser(user, CookieType.Stoken) + .UsingDynamicSecret2(SaltType.PROD, options, ApiEndpoints.AccountCookieAccountInfoBySToken) + .TryCatchGetFromJsonAsync>(logger, token) + .ConfigureAwait(false); + + return resp?.Data; + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/UidCookieToken.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/UidCookieToken.cs new file mode 100644 index 00000000..d6782b87 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/UidCookieToken.cs @@ -0,0 +1,22 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Web.Hoyolab.Passport; + +/// +/// uid 与 cookie token +/// +public class UidCookieToken +{ + /// + /// Uid + /// + [JsonPropertyName("uid")] + public string Uid { get; set; } = default!; + + /// + /// CookieToken + /// + [JsonPropertyName("cookie_token")] + public string CookieToken { get; set; } = default!; +} \ No newline at end of file