150 lines
4.8 KiB
C#
150 lines
4.8 KiB
C#
using Microsoft.Extensions.Options;
|
|
using SPMS.Application.DTOs.Auth;
|
|
using SPMS.Application.Interfaces;
|
|
using SPMS.Application.Settings;
|
|
using SPMS.Domain.Common;
|
|
using SPMS.Domain.Exceptions;
|
|
using SPMS.Domain.Interfaces;
|
|
|
|
namespace SPMS.Application.Services;
|
|
|
|
public class AuthService : IAuthService
|
|
{
|
|
private readonly IAdminRepository _adminRepository;
|
|
private readonly IUnitOfWork _unitOfWork;
|
|
private readonly IJwtService _jwtService;
|
|
private readonly JwtSettings _jwtSettings;
|
|
|
|
public AuthService(
|
|
IAdminRepository adminRepository,
|
|
IUnitOfWork unitOfWork,
|
|
IJwtService jwtService,
|
|
IOptions<JwtSettings> jwtSettings)
|
|
{
|
|
_adminRepository = adminRepository;
|
|
_unitOfWork = unitOfWork;
|
|
_jwtService = jwtService;
|
|
_jwtSettings = jwtSettings.Value;
|
|
}
|
|
|
|
public async Task<LoginResponseDto> LoginAsync(LoginRequestDto request)
|
|
{
|
|
// 1. 이메일로 관리자 조회
|
|
var admin = await _adminRepository.GetByEmailAsync(request.Email);
|
|
if (admin is null)
|
|
{
|
|
throw new SpmsException(
|
|
ErrorCodes.LoginFailed,
|
|
"이메일 또는 비밀번호가 일치하지 않습니다.",
|
|
401);
|
|
}
|
|
|
|
// 2. 삭제된 계정 확인
|
|
if (admin.IsDeleted)
|
|
{
|
|
throw new SpmsException(
|
|
ErrorCodes.LoginFailed,
|
|
"이메일 또는 비밀번호가 일치하지 않습니다.",
|
|
401);
|
|
}
|
|
|
|
// 3. 비밀번호 검증 (BCrypt)
|
|
if (!BCrypt.Net.BCrypt.Verify(request.Password, admin.Password))
|
|
{
|
|
throw new SpmsException(
|
|
ErrorCodes.LoginFailed,
|
|
"이메일 또는 비밀번호가 일치하지 않습니다.",
|
|
401);
|
|
}
|
|
|
|
// 4. 토큰 생성
|
|
var accessToken = _jwtService.GenerateAccessToken(
|
|
admin.Id,
|
|
admin.Role.ToString());
|
|
var refreshToken = _jwtService.GenerateRefreshToken();
|
|
|
|
// 5. Refresh Token 및 최종 로그인 시간 저장
|
|
admin.RefreshToken = refreshToken;
|
|
admin.RefreshTokenExpiresAt = DateTime.UtcNow.AddDays(_jwtSettings.RefreshTokenExpiryDays);
|
|
admin.LastLoginAt = DateTime.UtcNow;
|
|
_adminRepository.Update(admin);
|
|
await _unitOfWork.SaveChangesAsync();
|
|
|
|
// 6. 응답 반환
|
|
return new LoginResponseDto
|
|
{
|
|
AccessToken = accessToken,
|
|
RefreshToken = refreshToken,
|
|
ExpiresIn = _jwtSettings.ExpiryMinutes * 60,
|
|
Admin = new AdminInfoDto
|
|
{
|
|
AdminCode = admin.AdminCode,
|
|
Email = admin.Email,
|
|
Name = admin.Name,
|
|
Role = admin.Role.ToString()
|
|
}
|
|
};
|
|
}
|
|
|
|
public async Task<TokenRefreshResponseDto> RefreshTokenAsync(TokenRefreshRequestDto request)
|
|
{
|
|
// 1. Refresh Token으로 관리자 조회
|
|
var admin = await _adminRepository.GetByRefreshTokenAsync(request.RefreshToken);
|
|
if (admin is null)
|
|
{
|
|
throw new SpmsException(
|
|
ErrorCodes.Unauthorized,
|
|
"유효하지 않은 Refresh Token입니다.",
|
|
401);
|
|
}
|
|
|
|
// 2. Refresh Token 만료 확인
|
|
if (admin.RefreshTokenExpiresAt < DateTime.UtcNow)
|
|
{
|
|
throw new SpmsException(
|
|
ErrorCodes.Unauthorized,
|
|
"Refresh Token이 만료되었습니다. 다시 로그인해주세요.",
|
|
401);
|
|
}
|
|
|
|
// 3. 새 토큰 생성
|
|
var newAccessToken = _jwtService.GenerateAccessToken(
|
|
admin.Id,
|
|
admin.Role.ToString());
|
|
var newRefreshToken = _jwtService.GenerateRefreshToken();
|
|
|
|
// 4. 새 Refresh Token 저장 (Rotation)
|
|
admin.RefreshToken = newRefreshToken;
|
|
admin.RefreshTokenExpiresAt = DateTime.UtcNow.AddDays(_jwtSettings.RefreshTokenExpiryDays);
|
|
_adminRepository.Update(admin);
|
|
await _unitOfWork.SaveChangesAsync();
|
|
|
|
// 5. 응답 반환
|
|
return new TokenRefreshResponseDto
|
|
{
|
|
AccessToken = newAccessToken,
|
|
RefreshToken = newRefreshToken,
|
|
ExpiresIn = _jwtSettings.ExpiryMinutes * 60
|
|
};
|
|
}
|
|
|
|
public async Task LogoutAsync(long adminId)
|
|
{
|
|
// 1. 관리자 조회
|
|
var admin = await _adminRepository.GetByIdAsync(adminId);
|
|
if (admin is null)
|
|
{
|
|
throw new SpmsException(
|
|
ErrorCodes.NotFound,
|
|
"관리자를 찾을 수 없습니다.",
|
|
404);
|
|
}
|
|
|
|
// 2. Refresh Token 무효화
|
|
admin.RefreshToken = null;
|
|
admin.RefreshTokenExpiresAt = null;
|
|
_adminRepository.Update(admin);
|
|
await _unitOfWork.SaveChangesAsync();
|
|
}
|
|
}
|