90 lines
2.8 KiB
C#
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;
|
|
}
|
|
}
|
|
|
|
|