Files
KJD/Work/Store.Api/AuthApi.cs
T
2026-11-05 18:50:13 +08:00

90 lines
2.8 KiB
C#

using CsharpHttpHelper;
using System;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
namespace Work.Store.Api;
public sealed class LoginRequest
{
[Required]
public string UserName { get; set; }
/// <summary>UTF-8 密码的 32 位大写 MD5,由调用方计算。</summary>
[Required]
public string Password { get; set; }
[StringLength(100)]
public string DeviceId { get; set; }
[StringLength(100)]
public string DeviceName { get; set; }
}
public sealed class LoginResult
{
public string TokenType { get; set; } = "Bearer";
public string Token { get; set; }
public uint ExpiresInMinutes { get; set; }
}
public sealed class AuthApi
{
private readonly ApiTransport _transport;
private readonly string _deviceId;
public AuthApi(string baseUrl) : this(new ApiTransport(baseUrl)) { }
public AuthApi(ApiTransport transport, string deviceId = null)
{
_transport = transport ?? throw new ArgumentNullException(nameof(transport));
_deviceId = deviceId;
}
/// <summary>同步登录,凭据快照仅存内存,供 401 时自动重登;界面调用需放在后台线程。</summary>
public LoginResult Login(LoginRequest request)
{
if (request == null) throw new ArgumentNullException(nameof(request));
var credentials = new LoginRequest
{
UserName = request.UserName,
Password = request.Password,
DeviceId = _deviceId ?? request.DeviceId,
DeviceName = request.DeviceName
};
_transport.SessionGate.Wait();
try
{
var result = LoginCore(_transport, credentials);
_transport.SetSession(result.Token, credentials);
return result;
}
finally { _transport.SessionGate.Release(); }
}
internal static LoginResult LoginCore(ApiTransport transport, LoginRequest request)
{
var result = transport.GetRequired<LoginResult>("Auth/Login", MethodType.Post, request, authenticated: false);
return ValidateToken(result);
}
internal static async Task<LoginResult> LoginCoreAsync(ApiTransport transport, LoginRequest request)
{
var result = await transport.GetRequiredAsync<LoginResult>("Auth/Login", MethodType.Post, request, authenticated: false).ConfigureAwait(false);
return ValidateToken(result);
}
private static LoginResult ValidateToken(LoginResult result)
{
if (result == null || string.IsNullOrWhiteSpace(result.Token) ||
result.Token.IndexOfAny(new[] { '\r', '\n' }) >= 0 ||
!string.Equals(result.TokenType, "Bearer", StringComparison.OrdinalIgnoreCase))
throw new HttpException("登录响应缺少有效的 Bearer Token。");
return result;
}
}