| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- using System.ComponentModel.DataAnnotations;
- using System.ComponentModel.DataAnnotations.Schema;
- using System.Security.Cryptography;
- using Domain.Entities.Members.ValueObject;
- using Domain.Entities.Wallets;
- using NanoidDotNet;
- /// <summary>
- /// 사용자단 일반 회원 정보
- /// </summary>
- namespace Domain.Entities.Members
- {
- public class Member
- {
- [Key]
- public int ID { get; private set; }
- // 회원 등급 정보
- [ForeignKey(nameof(MemberGradeID))]
- public virtual MemberGrade? MemberGrade { get; private set; }
- // 알림/약관 동의 여부
- public virtual MemberApprove MemberApprove { get; private set; } = null!;
- // 회원 집계 정보
- public virtual MemberStats MemberStats { get; private set; } = null!;
- // 지갑
- public virtual Wallet? Wallet { get; private set; }
- // 채널
- public virtual Channel? Channel { get; private set; }
- public int? MemberGradeID { get; private set; }
- public string SID { get; private set; } = Nanoid.Generate(size: 8); // 무작위 문자열
- public string Email { get; private set; } = default!;
- public string? Name { get; private set; }
- public string? FullName { get; private set; }
- public string? FirstName { get; private set; }
- public string? LastName { get; private set; }
- public string? Password { get; private set; } = string.Empty; // 관리자 수정 시 필요
- public string? PasswordHash { get; private set; }
- public string? Intro { get; private set; }
- public string? Summary { get; private set; }
- public string? Phone { get; private set; }
- public DateOnly? Birthday { get; private set; }
- public Gender? Gender { get; private set; }
- public string? Thumb { get; private set; }
- public string? Icon { get; private set; }
- public bool IsEmailVerified { get; private set; } = false;
- public bool IsAuthCertified { get; private set; } = false;
- public bool IsDenied { get; private set; } = false;
- public bool IsAdmin { get; private set; } = false;
- public bool IsWithdraw { get; private set; } = false;
- public bool IsCreator { get; private set; } = false;
- public string? DeviceInfo { get; private set; }
- public string? SignupIP { get; private set; }
- public string? LastLoginIp { get; private set; }
- public string? IpAddress { get; private set; }
- public string? UserAgent { get; private set; }
- public DateTime? LastLoginAt { get; private set; }
- public DateTime? LastEmailChangedAt { get; private set; }
- public DateTime? LastNameChangedAt { get; private set; }
- public DateTime? LastSummaryChangedAt { get; private set; }
- public DateTime? LastIntroChangedAt { get; private set; }
- public DateTime? EmailVerifiedAt { get; private set; }
- public DateTime? AuthCertifiedAt { get; private set; }
- public DateTime PasswordUpdatedAt { get; private set; }
- public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;
- public DateTime? UpdatedAt { get; private set; }
- public DateTime? DeletedAt { get; private set; }
- public DateTime? DeniedAt { get; private set; }
- private Member() { }
- private Member(string email)
- {
- if (string.IsNullOrWhiteSpace(email))
- {
- throw new ArgumentException("Email is required.", nameof(email));
- }
- if (email.Length > 60)
- {
- throw new ArgumentOutOfRangeException(nameof(email));
- }
- Email = email;
- PasswordUpdatedAt = DateTime.UtcNow;
- }
- public static Member Create(string email)
- {
- return new(email);
- }
- public static Member Create(string email, string password)
- {
- var member = new Member(email);
- member.SetPassword(password);
- return member;
- }
- public void SetPassword(string password)
- {
- PasswordHash = HashPassword(password);
- PasswordUpdatedAt = DateTime.UtcNow;
- }
- public bool VerifyPassword(string password)
- {
- if (string.IsNullOrEmpty(PasswordHash))
- {
- return false;
- }
- return VerifyHashedPassword(PasswordHash, password);
- }
- // PBKDF2 (RFC 2898) - 순수 C# 비밀번호 해싱
- private static string HashPassword(string password)
- {
- var salt = RandomNumberGenerator.GetBytes(16);
- var hash = Rfc2898DeriveBytes.Pbkdf2(
- password,
- salt,
- iterations: 100_000,
- HashAlgorithmName.SHA256,
- outputLength: 32
- );
- // salt + hash → Base64
- var combined = new byte[salt.Length + hash.Length];
- Buffer.BlockCopy(salt, 0, combined, 0, salt.Length);
- Buffer.BlockCopy(hash, 0, combined, salt.Length, hash.Length);
- return Convert.ToBase64String(combined);
- }
- private static bool VerifyHashedPassword(string hashedPassword, string password)
- {
- var combined = Convert.FromBase64String(hashedPassword);
- if (combined.Length != 48) // 16(salt) + 32(hash)
- {
- return false;
- }
- var salt = combined[..16];
- var storedHash = combined[16..];
- var hash = Rfc2898DeriveBytes.Pbkdf2(
- password,
- salt,
- iterations: 100_000,
- HashAlgorithmName.SHA256,
- outputLength: 32
- );
- return CryptographicOperations.FixedTimeEquals(storedHash, hash);
- }
- }
- }
|