mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
support PROD salt for #207
This commit is contained in:
@@ -22,3 +22,8 @@ internal abstract class Md5Convert
|
|||||||
return System.Convert.ToHexString(hash);
|
return System.Convert.ToHexString(hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class RSAConvert
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -16,33 +16,6 @@ namespace Snap.Hutao.Core;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class CoreEnvironment
|
internal static class CoreEnvironment
|
||||||
{
|
{
|
||||||
// 计算过程:https://github.com/UIGF-org/Hoyolab.Salt
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 动态密钥1的K2盐
|
|
||||||
/// </summary>
|
|
||||||
public const string DynamicSecretK2Salt = "fdv0fY9My9eA7MR0NpjGP9RjueFvjUSQ";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 动态密钥1的LK2盐
|
|
||||||
/// </summary>
|
|
||||||
public const string DynamicSecretLK2Salt = "jEpJb9rRARU2rXDA9qYbZ3selxkuct9a";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 动态密钥2的X4盐
|
|
||||||
/// </summary>
|
|
||||||
public const string DynamicSecretX4Salt = "xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 动态密钥2的X6盐
|
|
||||||
/// </summary>
|
|
||||||
public const string DynamicSecretX6Salt = "t0qEgfub6cvueAPgR5m9aQWWVciEer7v";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// LoginApi的盐
|
|
||||||
/// </summary>
|
|
||||||
public const string DynamicSecretPRODSalt = "JwYDpKvLj6MrMqqYU6jTKF17KNO2PXoS";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 米游社请求UA
|
/// 米游社请求UA
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -55,6 +28,7 @@ internal static class CoreEnvironment
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 动态密钥
|
/// 动态密钥
|
||||||
|
/// https://github.com/UIGF-org/Hoyolab.Salt
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly ImmutableDictionary<SaltType, string> DynamicSecrets = new Dictionary<SaltType, string>()
|
public static readonly ImmutableDictionary<SaltType, string> DynamicSecrets = new Dictionary<SaltType, string>()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -129,10 +129,15 @@ internal static class ApiEndpoints
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const string UserGameRolesByCookie = $"{ApiTaKumiBindingApi}/getUserGameRolesByCookie?game_biz=hk4e_cn";
|
public const string UserGameRolesByCookie = $"{ApiTaKumiBindingApi}/getUserGameRolesByCookie?game_biz=hk4e_cn";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户游戏角色
|
||||||
|
/// </summary>
|
||||||
|
public const string UserGameRolesByStoken = $"{ApiTaKumiBindingApi}/getUserGameRolesByStoken";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// AuthKey
|
/// AuthKey
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string GenAuthKey = $"{ApiTaKumiBindingApi}/genAuthKey";
|
public const string BindingGenAuthKey = $"{ApiTaKumiBindingApi}/genAuthKey";
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Auth
|
#region Auth
|
||||||
@@ -179,7 +184,7 @@ internal static class ApiEndpoints
|
|||||||
// https://sdk-static.mihoyo.com/hk4e_cn/mdk/launcher/api/content?key=eYd89JmJ&language=zh-cn&launcher_id=18
|
// https://sdk-static.mihoyo.com/hk4e_cn/mdk/launcher/api/content?key=eYd89JmJ&language=zh-cn&launcher_id=18
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region ma-cn-session
|
#region LoginApi
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取 CookieToken
|
/// 获取 CookieToken
|
||||||
@@ -192,8 +197,30 @@ internal static class ApiEndpoints
|
|||||||
public const string AccountVerifyLtoken = $"{PassportApiV4}/account/ma-cn-session/web/verifyLtoken";
|
public const string AccountVerifyLtoken = $"{PassportApiV4}/account/ma-cn-session/web/verifyLtoken";
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region WebAccountApi
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 通过通行证Cookie登录
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>通行证Cookie登录</returns>
|
||||||
|
public static string AccountLoginByCookie()
|
||||||
|
{
|
||||||
|
return $"{WebApiAccountApi}/login_by_cookie?t={DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}";
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region App
|
#region App
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 另一个AuthKey
|
||||||
|
/// </summary>
|
||||||
|
public const string AppAccountGenAuthKey = $"{AppAuthApi}/genAuthKey";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 通过 Stoken 获取 Ltoken
|
||||||
|
/// </summary>
|
||||||
|
public const string AppAccountGetLTokenBySToken = $"{AppAuthApi}/getLTokenBySToken";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 小组件数据
|
/// 小组件数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -209,6 +236,7 @@ internal static class ApiEndpoints
|
|||||||
private const string ApiTakumiRecordApi = $"{ApiTakumiRecord}/game_record/app/genshin/api";
|
private const string ApiTakumiRecordApi = $"{ApiTakumiRecord}/game_record/app/genshin/api";
|
||||||
|
|
||||||
private const string App = "https://app.mihoyo.com";
|
private const string App = "https://app.mihoyo.com";
|
||||||
|
private const string AppAuthApi = $"{App}/account/auth/api";
|
||||||
private const string AppCardApi = $"{App}/game_record/app/card/api";
|
private const string AppCardApi = $"{App}/game_record/app/card/api";
|
||||||
|
|
||||||
private const string BbsApi = "https://bbs-api.mihoyo.com";
|
private const string BbsApi = "https://bbs-api.mihoyo.com";
|
||||||
@@ -224,5 +252,8 @@ internal static class ApiEndpoints
|
|||||||
private const string SdkStatic = "https://sdk-static.mihoyo.com";
|
private const string SdkStatic = "https://sdk-static.mihoyo.com";
|
||||||
private const string SdkStaticLauncherApi = $"{SdkStatic}/hk4e_cn/mdk/launcher/api";
|
private const string SdkStaticLauncherApi = $"{SdkStatic}/hk4e_cn/mdk/launcher/api";
|
||||||
|
|
||||||
|
private const string WebApiAccount = "https://webapi.account.mihoyo.com";
|
||||||
|
private const string WebApiAccountApi = $"{WebApiAccount}/Api";
|
||||||
|
|
||||||
private const string AnnouncementQuery = "game=hk4e&game_biz=hk4e_cn&lang=zh-cn&bundle_id=hk4e_cn&platform=pc®ion=cn_gf01&level=55&uid=100000000";
|
private const string AnnouncementQuery = "game=hk4e&game_biz=hk4e_cn&lang=zh-cn&bundle_id=hk4e_cn&platform=pc®ion=cn_gf01&level=55&uid=100000000";
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
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.Hoyolab.Takumi.Binding;
|
||||||
|
using Snap.Hutao.Web.Response;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.App.Account;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 账户客户端
|
||||||
|
/// </summary>
|
||||||
|
[HttpClient(HttpClientConfigration.XRpc)]
|
||||||
|
internal class AccountClient
|
||||||
|
{
|
||||||
|
private readonly HttpClient httpClient;
|
||||||
|
private readonly JsonSerializerOptions options;
|
||||||
|
private readonly ILogger<AccountClient> logger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的账户客户端
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="httpClient">http客户端</param>
|
||||||
|
/// <param name="options">选项</param>
|
||||||
|
/// <param name="logger">日志器</param>
|
||||||
|
public AccountClient(HttpClient httpClient, JsonSerializerOptions options, ILogger<AccountClient> logger)
|
||||||
|
{
|
||||||
|
this.httpClient = httpClient;
|
||||||
|
this.options = options;
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步生成祈愿验证密钥
|
||||||
|
/// 需要stoken
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">用户</param>
|
||||||
|
/// <param name="data">提交数据</param>
|
||||||
|
/// <param name="token">取消令牌</param>
|
||||||
|
/// <returns>用户角色信息</returns>
|
||||||
|
[ApiInformation(Cookie = CookieType.Stoken, Salt = SaltType.K2)]
|
||||||
|
public async Task<GameAuthKey?> GenerateAuthenticationKeyAsync(User user, GenAuthKeyData data, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
Response<GameAuthKey>? resp = await httpClient
|
||||||
|
.SetUser(user, CookieType.Stoken)
|
||||||
|
.SetReferer("https://app.mihoyo.com")
|
||||||
|
.UsingDynamicSecret1(SaltType.K2)
|
||||||
|
.TryCatchPostAsJsonAsync<GenAuthKeyData, Response<GameAuthKey>>(ApiEndpoints.AppAccountGenAuthKey, data, options, logger, token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
return resp?.Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步获取 Ltoken
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">用户</param>
|
||||||
|
/// <param name="token">取消令牌</param>
|
||||||
|
/// <returns>Ltoken</returns>
|
||||||
|
[ApiInformation(Cookie = CookieType.Stoken, Salt = SaltType.PROD)]
|
||||||
|
public async Task<string?> GetLTokenBySTokenAsync(User user, CancellationToken token)
|
||||||
|
{
|
||||||
|
Response<LtokenWrapper>? resp = await httpClient
|
||||||
|
.SetUser(user, CookieType.Stoken)
|
||||||
|
.UsingDynamicSecret2(SaltType.PROD, options, ApiEndpoints.AppAccountGetLTokenBySToken)
|
||||||
|
.TryCatchGetFromJsonAsync<Response<LtokenWrapper>>(token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
return resp?.Data?.Ltoken;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
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.Hoyolab.Takumi.Binding;
|
||||||
|
using Snap.Hutao.Web.Response;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.App.Account;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ltoken 包装器
|
||||||
|
/// </summary>
|
||||||
|
public class LtokenWrapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Ltoken
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("ltoken")]
|
||||||
|
public string Ltoken { get; set; } = default!;
|
||||||
|
}
|
||||||
@@ -39,7 +39,7 @@ internal class PassportClient
|
|||||||
/// <param name="user">用户</param>
|
/// <param name="user">用户</param>
|
||||||
/// <param name="token">取消令牌</param>
|
/// <param name="token">取消令牌</param>
|
||||||
/// <returns>验证信息</returns>
|
/// <returns>验证信息</returns>
|
||||||
[ApiInformation(Cookie = CookieType.Ltoken)]
|
[ApiInformation(Cookie = CookieType.All)]
|
||||||
public async Task<VerifyInformation?> VerifyLtokenAsync(User user, CancellationToken token)
|
public async Task<VerifyInformation?> VerifyLtokenAsync(User user, CancellationToken token)
|
||||||
{
|
{
|
||||||
Response<VerifyInformation>? response = await httpClient
|
Response<VerifyInformation>? response = await httpClient
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace Snap.Hutao.Web.Hoyolab.Takumi.Auth;
|
|||||||
/// 账户信息
|
/// 账户信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SuppressMessage("", "SA1600")]
|
[SuppressMessage("", "SA1600")]
|
||||||
public class AccountInfo
|
public class ActionTicketAccountInfo
|
||||||
{
|
{
|
||||||
[JsonPropertyName("is_realname")]
|
[JsonPropertyName("is_realname")]
|
||||||
public bool IsRealname { get; set; }
|
public bool IsRealname { get; set; }
|
||||||
@@ -29,5 +29,5 @@ public class ActionTicketWrapper
|
|||||||
/// 账户信息
|
/// 账户信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("account_info")]
|
[JsonPropertyName("account_info")]
|
||||||
public AccountInfo AccountInfo { get; set; } = default!;
|
public ActionTicketAccountInfo AccountInfo { get; set; } = default!;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,4 +69,21 @@ internal class BindingClient
|
|||||||
|
|
||||||
return EnumerableExtension.EmptyIfNull(resp?.Data?.List);
|
return EnumerableExtension.EmptyIfNull(resp?.Data?.List);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取用户角色信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">用户</param>
|
||||||
|
/// <param name="token">取消令牌</param>
|
||||||
|
/// <returns>用户角色信息</returns>
|
||||||
|
[ApiInformation(Cookie = CookieType.Stoken)]
|
||||||
|
public async Task<List<UserGameRole>> GetUserGameRolesByStokenAsync(User user, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
Response<ListWrapper<UserGameRole>>? resp = await httpClient
|
||||||
|
.SetUser(user, CookieType.Stoken)
|
||||||
|
.TryCatchGetFromJsonAsync<Response<ListWrapper<UserGameRole>>>(ApiEndpoints.UserGameRolesByStoken, options, logger, token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
return EnumerableExtension.EmptyIfNull(resp?.Data?.List);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
|
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
|
||||||
using Snap.Hutao.Model.Entity;
|
using Snap.Hutao.Model.Entity;
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Annotation;
|
||||||
using Snap.Hutao.Web.Hoyolab.DynamicSecret;
|
using Snap.Hutao.Web.Hoyolab.DynamicSecret;
|
||||||
using Snap.Hutao.Web.Response;
|
using Snap.Hutao.Web.Response;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
@@ -40,13 +41,14 @@ internal class BindingClient2
|
|||||||
/// <param name="data">提交数据</param>
|
/// <param name="data">提交数据</param>
|
||||||
/// <param name="token">取消令牌</param>
|
/// <param name="token">取消令牌</param>
|
||||||
/// <returns>用户角色信息</returns>
|
/// <returns>用户角色信息</returns>
|
||||||
|
[ApiInformation(Cookie = CookieType.Stoken, Salt = SaltType.K2)]
|
||||||
public async Task<GameAuthKey?> GenerateAuthenticationKeyAsync(User user, GenAuthKeyData data, CancellationToken token = default)
|
public async Task<GameAuthKey?> GenerateAuthenticationKeyAsync(User user, GenAuthKeyData data, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
Response<GameAuthKey>? resp = await httpClient
|
Response<GameAuthKey>? resp = await httpClient
|
||||||
.SetUser(user, CookieType.Stoken)
|
.SetUser(user, CookieType.Stoken)
|
||||||
.SetReferer("https://app.mihoyo.com")
|
.SetReferer("https://app.mihoyo.com")
|
||||||
.UsingDynamicSecret1(SaltType.LK2)
|
.UsingDynamicSecret1(SaltType.K2)
|
||||||
.TryCatchPostAsJsonAsync<GenAuthKeyData, Response<GameAuthKey>>(ApiEndpoints.GenAuthKey, data, options, logger, token)
|
.TryCatchPostAsJsonAsync<GenAuthKeyData, Response<GameAuthKey>>(ApiEndpoints.BindingGenAuthKey, data, options, logger, token)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
return resp?.Data;
|
return resp?.Data;
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ namespace Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 验证密钥提交数据
|
/// 验证密钥提交数据
|
||||||
|
/// im_css?
|
||||||
|
/// {"auth_appid":"im_ccs","game_biz":"bbs_cn","game_uid":0,"region":""}
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class GenAuthKeyData
|
public sealed class GenAuthKeyData
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ public enum KnownReturnCode : int
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
RET_NEED_AIGIS = -3101,
|
RET_NEED_AIGIS = -3101,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 登录信息已失效,请重新登录
|
||||||
|
/// </summary>
|
||||||
|
LoginDataOutdated = -262,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 访问过于频繁
|
/// 访问过于频繁
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user