This commit is contained in:
2026-04-16 15:19:48 +08:00
commit 1c892259a9
127 changed files with 17390 additions and 0 deletions
@@ -0,0 +1,115 @@
using System.Security.Cryptography;
using LiveRecorder.Application.Abstractions.Auth;
using LiveRecorder.Application.Abstractions.Persistence;
using LiveRecorder.Application.Common;
using LiveRecorder.Application.Models.Auth;
using LiveRecorder.Domain.Entities;
namespace LiveRecorder.Application.Services;
public sealed class AuthService : IAuthService
{
private readonly IUserAccountRepository _userAccountRepository;
private readonly IUserSessionRepository _userSessionRepository;
private readonly IUnitOfWork _unitOfWork;
public AuthService(
IUserAccountRepository userAccountRepository,
IUserSessionRepository userSessionRepository,
IUnitOfWork unitOfWork)
{
_userAccountRepository = userAccountRepository;
_userSessionRepository = userSessionRepository;
_unitOfWork = unitOfWork;
}
public async Task<LoginResponse> LoginAsync(LoginRequest request, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);
var username = request.Username.Trim();
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(request.Password))
{
throw new InvalidOperationException("用户名和密码不能为空。");
}
var user = await _userAccountRepository.GetByUsernameAsync(username, cancellationToken)
?? throw new InvalidOperationException("用户名或密码错误。");
if (!user.IsActive || !PasswordHasher.Verify(request.Password, user.PasswordHash))
{
throw new InvalidOperationException("用户名或密码错误。");
}
var now = DateTimeOffset.UtcNow;
var token = Convert.ToHexString(RandomNumberGenerator.GetBytes(32));
var expiresAt = now.AddHours(12);
var session = new UserSession(user.Id, token, expiresAt, now);
await _userSessionRepository.AddAsync(session, cancellationToken);
await _unitOfWork.SaveChangesAsync(cancellationToken);
var authenticatedUser = new AuthenticatedUser
{
UserId = user.Id,
Username = user.Username,
DisplayName = user.DisplayName,
Token = token,
ExpiresAt = expiresAt
};
return new LoginResponse
{
Token = token,
ExpiresAt = expiresAt,
User = authenticatedUser
};
}
public async Task LogoutAsync(string token, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(token))
{
return;
}
var session = await _userSessionRepository.GetByTokenAsync(token, cancellationToken);
if (session is null)
{
return;
}
session.Revoke(DateTimeOffset.UtcNow);
_userSessionRepository.Update(session);
await _unitOfWork.SaveChangesAsync(cancellationToken);
}
public async Task<AuthenticatedUser?> ValidateTokenAsync(string token, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(token))
{
return null;
}
var session = await _userSessionRepository.GetByTokenAsync(token, cancellationToken);
if (session is null || !session.IsValid(DateTimeOffset.UtcNow))
{
return null;
}
var user = await _userAccountRepository.GetByIdAsync(session.UserAccountId, cancellationToken);
if (user is null || !user.IsActive)
{
return null;
}
return new AuthenticatedUser
{
UserId = user.Id,
Username = user.Username,
DisplayName = user.DisplayName,
Token = session.Token,
ExpiresAt = session.ExpiresAt
};
}
}