mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
support login api for #207
This commit is contained in:
@@ -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
|
||||
/// </summary>
|
||||
public const string DynamicSecretX6Salt = "t0qEgfub6cvueAPgR5m9aQWWVciEer7v";
|
||||
|
||||
/// <summary>
|
||||
/// LoginApi的盐
|
||||
/// </summary>
|
||||
public const string DynamicSecretPRODSalt = "JwYDpKvLj6MrMqqYU6jTKF17KNO2PXoS";
|
||||
|
||||
/// <summary>
|
||||
/// 米游社请求UA
|
||||
/// </summary>
|
||||
@@ -46,6 +53,18 @@ internal static class CoreEnvironment
|
||||
/// </summary>
|
||||
public const string HoyolabXrpcVersion = "2.40.1";
|
||||
|
||||
/// <summary>
|
||||
/// 动态密钥
|
||||
/// </summary>
|
||||
public static readonly ImmutableDictionary<SaltType, string> DynamicSecrets = new Dictionary<SaltType, string>()
|
||||
{
|
||||
[SaltType.K2] = "fdv0fY9My9eA7MR0NpjGP9RjueFvjUSQ",
|
||||
[SaltType.LK2] = "jEpJb9rRARU2rXDA9qYbZ3selxkuct9a",
|
||||
[SaltType.X4] = "xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs",
|
||||
[SaltType.X6] = "t0qEgfub6cvueAPgR5m9aQWWVciEer7v",
|
||||
[SaltType.PROD] = "JwYDpKvLj6MrMqqYU6jTKF17KNO2PXoS",
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
/// <summary>
|
||||
/// 标准UA
|
||||
/// </summary>
|
||||
|
||||
@@ -181,6 +181,11 @@ internal static class ApiEndpoints
|
||||
|
||||
#region ma-cn-session
|
||||
|
||||
/// <summary>
|
||||
/// 获取 CookieToken
|
||||
/// </summary>
|
||||
public const string AccountCookieAccountInfoBySToken = $"{PassportApi}/account/auth/api/getCookieAccountInfoBySToken";
|
||||
|
||||
/// <summary>
|
||||
/// 验证 Ltoken 有效性
|
||||
/// </summary>
|
||||
@@ -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";
|
||||
|
||||
@@ -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}";
|
||||
|
||||
@@ -20,7 +20,7 @@ internal abstract class DynamicSecretProvider2 : Md5Convert
|
||||
/// <returns>密钥</returns>
|
||||
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()
|
||||
{
|
||||
// 原汁原味
|
||||
|
||||
@@ -23,6 +23,11 @@ public enum SaltType
|
||||
/// </summary>
|
||||
X6,
|
||||
|
||||
/// <summary>
|
||||
/// PROD
|
||||
/// </summary>
|
||||
PROD,
|
||||
|
||||
/// <summary>
|
||||
/// K2
|
||||
/// </summary>
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 通行证客户端 XRPC 版
|
||||
/// </summary>
|
||||
[HttpClient(HttpClientConfigration.XRpc)]
|
||||
internal class PassportClient2
|
||||
{
|
||||
private readonly HttpClient httpClient;
|
||||
private readonly JsonSerializerOptions options;
|
||||
private readonly ILogger<PassportClient> logger;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的通行证客户端
|
||||
/// </summary>
|
||||
/// <param name="httpClient">http客户端</param>
|
||||
/// <param name="options">json序列化选项</param>
|
||||
/// <param name="logger">日志器</param>
|
||||
public PassportClient2(HttpClient httpClient, JsonSerializerOptions options, ILogger<PassportClient> logger)
|
||||
{
|
||||
this.httpClient = httpClient;
|
||||
this.options = options;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取 CookieToken
|
||||
/// </summary>
|
||||
/// <param name="user">用户</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>uid 与 cookie token</returns>
|
||||
[ApiInformation(Cookie = CookieType.Stoken, Salt = SaltType.PROD)]
|
||||
public async Task<UidCookieToken?> GetCookieAccountInfoBySTokenAsync(User user, CancellationToken token)
|
||||
{
|
||||
Response<UidCookieToken>? resp = await httpClient
|
||||
.SetUser(user, CookieType.Stoken)
|
||||
.UsingDynamicSecret2(SaltType.PROD, options, ApiEndpoints.AccountCookieAccountInfoBySToken)
|
||||
.TryCatchGetFromJsonAsync<Response<UidCookieToken>>(logger, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return resp?.Data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Web.Hoyolab.Passport;
|
||||
|
||||
/// <summary>
|
||||
/// uid 与 cookie token
|
||||
/// </summary>
|
||||
public class UidCookieToken
|
||||
{
|
||||
/// <summary>
|
||||
/// Uid
|
||||
/// </summary>
|
||||
[JsonPropertyName("uid")]
|
||||
public string Uid { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// CookieToken
|
||||
/// </summary>
|
||||
[JsonPropertyName("cookie_token")]
|
||||
public string CookieToken { get; set; } = default!;
|
||||
}
|
||||
Reference in New Issue
Block a user