1111
This commit is contained in:
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user