From 3c5f27ce6859894e02f9f33987e4f5f57200488b Mon Sep 17 00:00:00 2001 From: SHM Date: Thu, 17 Jul 2025 11:54:42 +0900 Subject: [PATCH] =?UTF-8?q?[=EC=84=B1=ED=98=84=EB=AA=A8]=20DBProvider?= =?UTF-8?q?=EC=B6=94=EA=B0=80.=20LoginContext=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/Config/WebApi.AuthApi.Config.json | 4 +- Projects/Config/log4net.config | 2 +- Projects/DLL/SystemX.Core.dll | Bin 55296 -> 51712 bytes .../dacpac/SystemX.DB.AccountDB.dacpac | Bin 3503 -> 3503 bytes .../Controller}/CommonController.cs | 23 +- .../AccountDB/Context/AccountDbContext.cs | 84 ------ .../AccountDB/Tables/TRefreshToken.cs | 11 - .../DB/DBContext/AccountDB/Tables/TRole.cs | 13 - .../DB/DBContext/AccountDB/Tables/TUser.cs | 19 -- .../SystemX.Core/Log4net/Log4net.cs | 2 +- .../Model/Auth/{LoginModel.cs => Login.cs} | 4 +- .../Model/Auth/{LogoutModel.cs => Logout.cs} | 4 +- .../Auth/{RegisterModel.cs => Register.cs} | 4 +- .../SystemX.Core/Model/Auth/UserModel.cs | 14 - .../Services/DBContextProvider.cs | 45 +++ .../SystemX.Core/SystemX.Core.csproj | 1 + .../SystemX.Core/Utils/DBUtils.cs | 2 +- Projects/WebApi/AuthApi/AuthApi.http | 6 - .../AuthApi/Controllers/AuthController.cs | 36 ++- Projects/WebApi/AuthApi/Program.cs | 30 +- .../WebApi/AuthApi/Services/AuthService.cs | 278 +++++++++--------- .../AccountDB/Context/AccountDbContext.cs | 6 +- 22 files changed, 265 insertions(+), 323 deletions(-) rename Projects/{WebApi/AuthApi/Controllers => SystemX.Core/SystemX.Core/Controller}/CommonController.cs (72%) delete mode 100644 Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Context/AccountDbContext.cs delete mode 100644 Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Tables/TRefreshToken.cs delete mode 100644 Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Tables/TRole.cs delete mode 100644 Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Tables/TUser.cs rename Projects/SystemX.Core/SystemX.Core/Model/Auth/{LoginModel.cs => Login.cs} (92%) rename Projects/SystemX.Core/SystemX.Core/Model/Auth/{LogoutModel.cs => Logout.cs} (85%) rename Projects/SystemX.Core/SystemX.Core/Model/Auth/{RegisterModel.cs => Register.cs} (92%) delete mode 100644 Projects/SystemX.Core/SystemX.Core/Model/Auth/UserModel.cs create mode 100644 Projects/SystemX.Core/SystemX.Core/Services/DBContextProvider.cs delete mode 100644 Projects/WebApi/AuthApi/AuthApi.http diff --git a/Projects/Config/WebApi.AuthApi.Config.json b/Projects/Config/WebApi.AuthApi.Config.json index d1ab4d3..60a1b11 100644 --- a/Projects/Config/WebApi.AuthApi.Config.json +++ b/Projects/Config/WebApi.AuthApi.Config.json @@ -18,7 +18,6 @@ "Port": 1433, "DBName": "AccountDB", "DBID": 1, - "DBContext": "AccountDB", "UserID": "SystemX", "Password": "X" }, @@ -26,8 +25,7 @@ "IP": "127.0.0.1", "Port": 1433, "DBName": "AccountDB_DEV", - "DBID": 2, - "DBContext": "AccountDB", + "DBID": 2, "UserID": "SystemX", "Password": "X" } diff --git a/Projects/Config/log4net.config b/Projects/Config/log4net.config index b970851..b7743e6 100644 --- a/Projects/Config/log4net.config +++ b/Projects/Config/log4net.config @@ -52,7 +52,7 @@ - + diff --git a/Projects/DLL/SystemX.Core.dll b/Projects/DLL/SystemX.Core.dll index 5ab830f7e02832f71c645e3be9016ceb10042403..cf2421f40ca92a584eeccbf9aa9e50f379c20c08 100644 GIT binary patch literal 51712 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

XHf9gEINb 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 HeH8c`2nOz| literal 55296 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 NM@;u)a{m_`_%HhbMqB^@ diff --git a/Projects/SystemX.Core/DBPatch/sqlScripts/dacpac/SystemX.DB.AccountDB.dacpac b/Projects/SystemX.Core/DBPatch/sqlScripts/dacpac/SystemX.DB.AccountDB.dacpac index 67cb16e7592f468ffedd2894d8c1c69075e430b2..3dca7fadca116c27294b5cde56dd1bc37797bd01 100644 GIT binary patch delta 660 zcmZ24ykr$eOwIVd^7hQR;ybtB{9S%>*3|D_Z(LtTIIIr-esacR z8HQN9k8PRz1(!~Wde;+witWYosN&SyOMDpY?IBq&Jg+i-g0QTkHBJ+iduB z;M*U^lgi<41+`qak6hopEa%zbI@JUoo}W{5c!k%Vi|-4Pj9WW1@n&D-J(ifWygS?% zTV%JTDc_d2dp_s?8L4@@Y|owjP|BL0_+#U=^}PAM>zz6m83$#WJLk_~juD*F@Oqod z_p(9>p zc~2X+XELT+Z(3~KqQ!KxCsO+VqrFp%{1)c>|CyJ(>R-Bp^OrRSzwc+;Z{+!V?(x1k z*_)~!J#{QU`$ghd!)MpdE3Cb1D&F31f00(gcwNvjaEIb+g=N}vly~t=Q#pUJtEFV; zoddis-L4zj40$!rTtApLQJ*7D&#k0!@d3MO3%@Hoon*c9^~!Tm?(rMs)gO4>`6ra{ pOKsDAlms<-D))16I;!U}0n^)fEWz|A9xFt;GT}7<3#RjW003h%Kmh;% delta 660 zcmZ24y! z4Za5b91>3*m#muQw`9p(>l-Rz4Cy(+zQ3|#zfAtcZOVCJ*{L;J>ucWIewUe_{rh_V z{F9HfVN(4U%gX$$KVk%Yr4>Z0}k{kAAF%DKoj%*)*s6}Cz3=)^ZS zqq>AA&oVCMoPFf_>SZ~P4&|vPaD4gMnql&=_f)*UQf8nC=M3d6st@+*p4@yh zHb&-x{7S_~zv}1w_GkQ^9+UFqKh`oxMTvh+&*SJQ%4BNCNaqYAt^;g#1jy>A#|7MS< z`?V$37Mg{U(>MZoPPRx$|9`Z1s*%sa?Ebrb$!q?lJJfzzW$^ocw*96rf7dKZdKLrI3M_>RGHNr6%e-bnc}TTi{{fDi!~x1E|qWR)^~q+XZ~XwcW#-M&p)}= zDa< : ControllerBase where T : WebCommonConfig { public readonly IServiceProvider _serviceProvider; public readonly IHttpContextAccessor _httpContextAccessor; - public readonly ConfigService? _configService; + public readonly ConfigService? _configService; protected static Guid guid { get; private set; } = Guid.NewGuid(); @@ -21,9 +28,9 @@ namespace AuthApi.Controllers _httpContextAccessor = httpContextAccessor; //service - _configService = _serviceProvider.GetService>(); + _configService = _serviceProvider.GetRequiredService>(); } - + /// /// Request 클라이언트 IP /// @@ -53,7 +60,7 @@ namespace AuthApi.Controllers /// protected virtual string GetMethodName([CallerMemberName] string callerMemberName = "") { - return callerMemberName; + return $"{callerMemberName}()"; } } } diff --git a/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Context/AccountDbContext.cs b/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Context/AccountDbContext.cs deleted file mode 100644 index f330eda..0000000 --- a/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Context/AccountDbContext.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.Collections.Generic; -using Microsoft.EntityFrameworkCore; -using SystemX.Core.DB.DBContext.AccountDB.Tables; - -namespace SystemX.Core.DB.DBContext.AccountDB.Context; - -public partial class AccountDbContext : DbContext -{ - public AccountDbContext() - { - } - - public AccountDbContext(DbContextOptions options) - : base(options) - { - } - - public virtual DbSet TRefreshTokens { get; set; } - - public virtual DbSet TRoles { get; set; } - - public virtual DbSet TUsers { get; set; } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) -#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263. - => optionsBuilder.UseSqlServer("server=127.0.0.1; user id=SystemX; password=X; database=AccountDB; TrustServerCertificate=true;"); - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.Entity(entity => - { - entity.HasKey(e => e.CAuid).HasName("PK__tRefresh__FBF0855465EB95AB"); - - entity.ToTable("tRefreshToken"); - - entity.Property(e => e.CAuid) - .HasMaxLength(250) - .HasColumnName("cAuid"); - entity.Property(e => e.CRefreshToken) - .HasMaxLength(1000) - .HasColumnName("cRefreshToken"); - }); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => e.CAuid).HasName("PK__tRole__FBF085540BB887D7"); - - entity.ToTable("tRole"); - - entity.Property(e => e.CAuid) - .HasMaxLength(250) - .HasColumnName("cAuid"); - entity.Property(e => e.CRoleId).HasColumnName("cRoleID"); - entity.Property(e => e.CRoleName) - .HasMaxLength(20) - .HasColumnName("cRoleName"); - }); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => e.CUserId).HasName("PK__tUser__A75DC19A721265FF"); - - entity.ToTable("tUser"); - - entity.Property(e => e.CUserId) - .HasMaxLength(50) - .HasColumnName("cUserID"); - entity.Property(e => e.CAuid) - .HasMaxLength(250) - .HasColumnName("cAuid"); - entity.Property(e => e.CCreateDateTime).HasColumnName("cCreateDateTime"); - entity.Property(e => e.CLastLoginDateTime).HasColumnName("cLastLoginDateTime"); - entity.Property(e => e.CPasswordHashed) - .HasMaxLength(250) - .HasColumnName("cPasswordHashed"); - entity.Property(e => e.CState).HasColumnName("cState"); - }); - - OnModelCreatingPartial(modelBuilder); - } - - partial void OnModelCreatingPartial(ModelBuilder modelBuilder); -} diff --git a/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Tables/TRefreshToken.cs b/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Tables/TRefreshToken.cs deleted file mode 100644 index c2578dc..0000000 --- a/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Tables/TRefreshToken.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace SystemX.Core.DB.DBContext.AccountDB.Tables; - -public partial class TRefreshToken -{ - public string CAuid { get; set; } = null!; - - public string CRefreshToken { get; set; } = null!; -} diff --git a/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Tables/TRole.cs b/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Tables/TRole.cs deleted file mode 100644 index 5404196..0000000 --- a/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Tables/TRole.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace SystemX.Core.DB.DBContext.AccountDB.Tables; - -public partial class TRole -{ - public string CAuid { get; set; } = null!; - - public byte CRoleId { get; set; } - - public string CRoleName { get; set; } = null!; -} diff --git a/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Tables/TUser.cs b/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Tables/TUser.cs deleted file mode 100644 index 4a10d36..0000000 --- a/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Tables/TUser.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace SystemX.Core.DB.DBContext.AccountDB.Tables; - -public partial class TUser -{ - public string CUserId { get; set; } = null!; - - public string CAuid { get; set; } = null!; - - public string CPasswordHashed { get; set; } = null!; - - public byte CState { get; set; } - - public DateTime CCreateDateTime { get; set; } - - public DateTime? CLastLoginDateTime { get; set; } -} diff --git a/Projects/SystemX.Core/SystemX.Core/Log4net/Log4net.cs b/Projects/SystemX.Core/SystemX.Core/Log4net/Log4net.cs index adef78f..4eb1b00 100644 --- a/Projects/SystemX.Core/SystemX.Core/Log4net/Log4net.cs +++ b/Projects/SystemX.Core/SystemX.Core/Log4net/Log4net.cs @@ -363,7 +363,7 @@ public static class Log4net - + diff --git a/Projects/SystemX.Core/SystemX.Core/Model/Auth/LoginModel.cs b/Projects/SystemX.Core/SystemX.Core/Model/Auth/Login.cs similarity index 92% rename from Projects/SystemX.Core/SystemX.Core/Model/Auth/LoginModel.cs rename to Projects/SystemX.Core/SystemX.Core/Model/Auth/Login.cs index 997084b..58d60b3 100644 --- a/Projects/SystemX.Core/SystemX.Core/Model/Auth/LoginModel.cs +++ b/Projects/SystemX.Core/SystemX.Core/Model/Auth/Login.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; namespace SystemX.Core.Model.Auth { //로그인 요청 모델 - public class LoginModel + public class Login { [Required] public string? UserID { get; set; } @@ -19,7 +19,7 @@ namespace SystemX.Core.Model.Auth } //로그인 응답 모델 - public class LoginResponseModel + public class LoginResponse { public string? UserID { get; set; } public UserRole Role { get; set; } diff --git a/Projects/SystemX.Core/SystemX.Core/Model/Auth/LogoutModel.cs b/Projects/SystemX.Core/SystemX.Core/Model/Auth/Logout.cs similarity index 85% rename from Projects/SystemX.Core/SystemX.Core/Model/Auth/LogoutModel.cs rename to Projects/SystemX.Core/SystemX.Core/Model/Auth/Logout.cs index bf8f7a1..85767d2 100644 --- a/Projects/SystemX.Core/SystemX.Core/Model/Auth/LogoutModel.cs +++ b/Projects/SystemX.Core/SystemX.Core/Model/Auth/Logout.cs @@ -8,13 +8,13 @@ using System.Threading.Tasks; namespace SystemX.Core.Model.Auth { - public class LogoutModel + public class Logout { [Required] public string? UserID { get; set; } } - public class LogoutResponseModel + public class LogoutResponse { public string? UserID { get; set; } diff --git a/Projects/SystemX.Core/SystemX.Core/Model/Auth/RegisterModel.cs b/Projects/SystemX.Core/SystemX.Core/Model/Auth/Register.cs similarity index 92% rename from Projects/SystemX.Core/SystemX.Core/Model/Auth/RegisterModel.cs rename to Projects/SystemX.Core/SystemX.Core/Model/Auth/Register.cs index 67085f8..738162c 100644 --- a/Projects/SystemX.Core/SystemX.Core/Model/Auth/RegisterModel.cs +++ b/Projects/SystemX.Core/SystemX.Core/Model/Auth/Register.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; namespace SystemX.Core.Model.Auth { //유저 등록 모델 - public class RegisterModel + public class Register { [Required] public string UserID { get; set; } = string.Empty; @@ -24,7 +24,7 @@ namespace SystemX.Core.Model.Auth public UserRole Role { get; set; } = UserRole.User; } - public class RegisterResponseModel + public class RegisterResponse { public string? UserID { get; set; } public UserRole Role { get; set; } diff --git a/Projects/SystemX.Core/SystemX.Core/Model/Auth/UserModel.cs b/Projects/SystemX.Core/SystemX.Core/Model/Auth/UserModel.cs deleted file mode 100644 index 660f847..0000000 --- a/Projects/SystemX.Core/SystemX.Core/Model/Auth/UserModel.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SystemX.Core.Model.Auth -{ - public class UserModel - { - //public TUser? TUser { get; set; } - //public TRole? TRole { get; set; } - } -} diff --git a/Projects/SystemX.Core/SystemX.Core/Services/DBContextProvider.cs b/Projects/SystemX.Core/SystemX.Core/Services/DBContextProvider.cs new file mode 100644 index 0000000..0e7e12d --- /dev/null +++ b/Projects/SystemX.Core/SystemX.Core/Services/DBContextProvider.cs @@ -0,0 +1,45 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SystemX.Core.Config.Model; + +namespace SystemX.Core.Services +{ + public class DbContextProvider + { + private Dictionary DBDictionary = new Dictionary(); + + public void SetDBList(IList? DBList) + { + if (DBList?.Count > 0) + { + foreach (var db in DBList) + { + if (DBDictionary.ContainsKey($"{db.DBName}") == false) + DBDictionary.Add($"{db.DBName}", db); + else + Log4net.WriteLine($"Exist key DB Dictionary {db.DBName}", LogType.Error); + } + } + } + + // 또는 context 직접 반환 (주의: caller가 Dispose 해야 함) + public TContext? GetDBContext(string dbName) where TContext : DbContext + { + if(DBDictionary.TryGetValue(dbName, out var db) == true) + { + var optionsBuilder = new DbContextOptionsBuilder(); + optionsBuilder.UseSqlServer(db.ConvertToConnectionString()); + + return (TContext)Activator.CreateInstance(typeof(TContext), optionsBuilder.Options); + } + + return null; + } + } +} diff --git a/Projects/SystemX.Core/SystemX.Core/SystemX.Core.csproj b/Projects/SystemX.Core/SystemX.Core/SystemX.Core.csproj index 3ffe46e..ed90dae 100644 --- a/Projects/SystemX.Core/SystemX.Core/SystemX.Core.csproj +++ b/Projects/SystemX.Core/SystemX.Core/SystemX.Core.csproj @@ -20,6 +20,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Projects/SystemX.Core/SystemX.Core/Utils/DBUtils.cs b/Projects/SystemX.Core/SystemX.Core/Utils/DBUtils.cs index eecdfe7..adb4a4c 100644 --- a/Projects/SystemX.Core/SystemX.Core/Utils/DBUtils.cs +++ b/Projects/SystemX.Core/SystemX.Core/Utils/DBUtils.cs @@ -12,7 +12,7 @@ public static class DBUtils /// public static string ConvertToConnectionString(this DataBase dbConfig) { - return $"server={dbConfig.IP},{dbConfig.Port}; user id={dbConfig.UserID}; password={dbConfig.Password}; database={dbConfig.DBName};"; + return $"server={dbConfig.IP},{dbConfig.Port}; user id={dbConfig.UserID}; password={dbConfig.Password}; database={dbConfig.DBName}; TrustServerCertificate=True;"; } } diff --git a/Projects/WebApi/AuthApi/AuthApi.http b/Projects/WebApi/AuthApi/AuthApi.http deleted file mode 100644 index 63f0da0..0000000 --- a/Projects/WebApi/AuthApi/AuthApi.http +++ /dev/null @@ -1,6 +0,0 @@ -@AuthApi_HostAddress = http://localhost:5177 - -GET {{AuthApi_HostAddress}}/weatherforecast/ -Accept: application/json - -### diff --git a/Projects/WebApi/AuthApi/Controllers/AuthController.cs b/Projects/WebApi/AuthApi/Controllers/AuthController.cs index 83c9889..7ea8a44 100644 --- a/Projects/WebApi/AuthApi/Controllers/AuthController.cs +++ b/Projects/WebApi/AuthApi/Controllers/AuthController.cs @@ -1,4 +1,5 @@ using AuthApi.Services; +using Azure.Core; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.IdentityModel.Tokens; @@ -6,15 +7,17 @@ using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using SystemX.Core; +using SystemX.Core.Controller; using SystemX.Core.Model.Auth; +using WebApi.Library.Config; namespace AuthApi.Controllers { [Tags("Auth")] [Route("api/auth")] [ApiController] - [ApiExplorerSettings(IgnoreApi = true)] - public class AuthController : CommonController + [ApiExplorerSettings(IgnoreApi = false)] + public class AuthController : CommonController { private readonly AuthService _authService; @@ -25,36 +28,39 @@ namespace AuthApi.Controllers _authService = authService; } - [HttpGet("/health")] + [HttpGet("health")] public async Task Health() { + Log4net.WriteLine($"[{GetRequestMethod()}:{GetMethodName()}] [Client IP:{GetClientIP()}] [RequestUrl:{GetRequestUrl()}]{Environment.NewLine}", LogType.CONTROLLER); await Task.CompletedTask; + return Results.Ok("Healthy"); } [HttpPost("regisger")] - public async Task Register([FromBody] RegisterModel request) + public async Task Register([FromBody] Register request) { - // Log4net.WriteLine(GetRequestLog(request).LogModelToString("Request Auth"), LogType.CONTROLLER); + Guid guid = Guid.NewGuid(); - RegisterResponseModel response = new RegisterResponseModel(); + Log4net.WriteLine($"[Request][{GetRequestMethod()}:{GetMethodName()}][Client IP:{GetClientIP()}][RequestUrl:{GetRequestUrl()}]::({guid}){Environment.NewLine} {request.ToJson()}", LogType.CONTROLLER); + RegisterResponse response = new RegisterResponse(); if (request?.UserID != null && request?.Password != null) { response = await _authService.CreateUser(request); } - // Log4net.WriteLine(GetResponseLog(response).LogModelToString("Response Auth"), LogType.CONTROLLER); + Log4net.WriteLine($"[Response]::({guid}){Environment.NewLine} {response.ToJson()}", LogType.CONTROLLER); return Results.Ok(response); } [HttpPost("login")] - public async Task Login([FromBody] LoginModel request) + public async Task Login([FromBody] Login request) { - // Log4net.WriteLine(GetRequestLog(request).LogModelToString("Request Auth"), LogType.CONTROLLER); + Log4net.WriteLine($"[Request]({guid}) api/auth/register{Environment.NewLine} {request.ToJson()}", LogType.CONTROLLER); - LoginResponseModel response = new LoginResponseModel(); + LoginResponse response = new LoginResponse(); response.UserID = request.UserID; response.EC = ERROR_CODE.EC_USER_LOGIN_FAILED; @@ -75,20 +81,20 @@ namespace AuthApi.Controllers await _authService.UpdateLoginInfo(request, response.RefreshToken); } - // Log4net.WriteLine(GetResponseLog(response).LogModelToString("Response Auth"), LogType.CONTROLLER); + Log4net.WriteLine($"[Response]({guid}) api/auth/register{Environment.NewLine} {response.ToJson()}", LogType.CONTROLLER); return Results.Ok(response); } [HttpPost("logout")] - public async Task Logout([FromBody] LogoutModel request) + public async Task Logout([FromBody] Logout request) { - // Log4net.WriteLine(GetRequestLog(request).LogModelToString("Request Auth"), LogType.CONTROLLER); + Log4net.WriteLine($"[Request]({guid}) api/auth/register{Environment.NewLine} {request.ToJson()}", LogType.CONTROLLER); var response = _authService.LogoutUser(request); await Task.CompletedTask; - // Log4net.WriteLine(GetResponseLog(response).LogModelToString("Response Auth"), LogType.CONTROLLER); + Log4net.WriteLine($"[Response]({guid}) api/auth/register{Environment.NewLine} {response.ToJson()}", LogType.CONTROLLER); return Results.Ok(response); } @@ -113,7 +119,7 @@ namespace AuthApi.Controllers }; } - private string GenerateJwtToken(LoginResponseModel loginResponseModel, bool isRefreshToken = false) + private string GenerateJwtToken(LoginResponse loginResponseModel, bool isRefreshToken = false) { var claims = new[] { diff --git a/Projects/WebApi/AuthApi/Program.cs b/Projects/WebApi/AuthApi/Program.cs index 5135652..cd0e835 100644 --- a/Projects/WebApi/AuthApi/Program.cs +++ b/Projects/WebApi/AuthApi/Program.cs @@ -1,10 +1,14 @@ using AuthApi.Services; using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; using Microsoft.IdentityModel.Tokens; +using System; using System.ComponentModel; using System.Text; using SystemX.Core.Services; using WebApi.Library.Config; +using WebApi.Library.DBContext.DB.DBContext.AccountDB.Context; string configDir = @"../../Config"; string configFileName = "WebApi.AuthApi.Config.json"; @@ -26,14 +30,18 @@ var builder = WebApplication.CreateBuilder(args); // Add services to the container. //singleton builder.Services.AddSingleton>(); -builder.Services.AddScoped(); //scoped +builder.Services.AddScoped(); + +//db +builder.Services.AddSingleton(); // Generic builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); +builder.Services.AddHttpContextAccessor(); //config preload, auth set ConfigService preloadConfig = new ConfigService(); @@ -61,7 +69,7 @@ if (preloadConfig.OpenConfig($@"{configDir}/{configFileName}") == true) ValidAudience = $"{config?.Auth?.audience}", IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes($"{config?.Auth?.accessTokenSecret}")) }; - }); + }); } else { @@ -84,6 +92,24 @@ if (configService?.OpenConfig($@"{configDir}/{configFileName}") == true) { serverUrl = $"{apiConfig?.Server?.Address}:{apiConfig?.Server?.Port}"; isIIS = apiConfig!.Server.IIS; + + using(var scoped = app.Services.CreateScope()) + { + var dbProvider = scoped.ServiceProvider.GetRequiredService(); + dbProvider?.SetDBList(apiConfig?.DataBase); + } + + //dbcontext set + //app.Use(async (context, next) => + //{ + // var dbProvider = context.RequestServices.GetService(); + // // + // dbProvider?.SetDBList(apiConfig?.DataBase); + // await next.Invoke(); + //}); + + //var dbProviderService = app.Services.GetService(); + //dbProviderService?.SetDBList(apiConfig?.DataBase); } } else diff --git a/Projects/WebApi/AuthApi/Services/AuthService.cs b/Projects/WebApi/AuthApi/Services/AuthService.cs index acced8d..29583ce 100644 --- a/Projects/WebApi/AuthApi/Services/AuthService.cs +++ b/Projects/WebApi/AuthApi/Services/AuthService.cs @@ -6,8 +6,8 @@ using SystemX.Core.Config.Model; using System.Data; using SystemX.Core.DB; using Microsoft.EntityFrameworkCore; -using SystemX.Core.DB.DBContext.AccountDB.Context; -using SystemX.Core.DB.DBContext.AccountDB.Tables; +using WebApi.Library.DBContext.DB.DBContext.AccountDB.Context; +using WebApi.Library.DBContext.DB.DBContext.AccountDB.Tables; namespace AuthApi.Services { @@ -16,26 +16,22 @@ namespace AuthApi.Services private readonly IServiceProvider _serviceProvider; private readonly IServiceScopeFactory _scopeFactory; private readonly ConfigService? _configService; - - private readonly DataBase? _accountDB; - - private static List Session = new List(); + + private static List Session = new List(); public AuthService(IServiceProvider serviceProvider, IServiceScopeFactory scopeFactory, ConfigService configSerice) { _serviceProvider = serviceProvider; _configService = configSerice; _scopeFactory = scopeFactory; - _accountDB = _configService?.GetConfig()?.DataBase?.Find(x => x.DBContext == "VpkiAccountDbContext"); } /// /// create new user /// - public async Task CreateUser(RegisterModel registerModel) - { - //response - RegisterResponseModel response = new RegisterResponseModel(); + public async Task CreateUser(Register registerModel) + { + RegisterResponse response = new RegisterResponse(); response.EC = ERROR_CODE.EC_USER_REGISTER_FAILED; response.UserID = registerModel.UserID; response.Role = registerModel.Role; @@ -44,39 +40,42 @@ namespace AuthApi.Services //context using (var scope = _scopeFactory.CreateScope()) { - var context = scope.ServiceProvider.GetRequiredService(); - if (context is not null) + var provider = scope.ServiceProvider.GetRequiredService(); + using (var context = GetAccountDBContext(provider, 1)) { - var user = await context.TUsers.AsNoTracking().Where(x => x.CUserId.ToLower() == registerModel.UserID.ToLower()).ToListAsync(); - if (user?.Count <= 0) + if (context is not null) { - string auid = Guid.NewGuid().ToString(); - //user - TUser newUser = new TUser + var user = await context.TUsers.AsNoTracking().Where(x => x.CUserId.ToLower() == registerModel.UserID.ToLower()).ToListAsync(); + if (user?.Count <= 0) { - CAuid = auid, - CUserId = registerModel.UserID, - CPasswordHashed = registerModel.Password, - CCreateDateTime = DateTime.Now, - CLastLoginDateTime = new DateTime() - }; - //role - TRole newUserRole = new TRole - { - CAuid = auid, - CRoleId = Convert.ToByte(registerModel.Role), - CRoleName = registerModel.Role.ToString() - }; - - using (var transaction = await context.CreateTransactionAsync()) - { - await context.AddAsync(newUser); - await context.AddAsync(newUserRole); - - var result = await context.CloseTransactionAsync(transaction); - if (result == true) + string auid = Guid.NewGuid().ToString(); + //user + TUser newUser = new TUser { - response.EC = ERROR_CODE.EC_OK; + CAuid = auid, + CUserId = registerModel.UserID, + CPasswordHashed = registerModel.Password, + CCreateDateTime = DateTime.Now, + CLastLoginDateTime = new DateTime() + }; + //role + TRole newUserRole = new TRole + { + CAuid = auid, + CRoleId = Convert.ToByte(registerModel.Role), + CRoleName = registerModel.Role.ToString() + }; + + using (var transaction = await context.CreateTransactionAsync()) + { + await context.AddAsync(newUser); + await context.AddAsync(newUserRole); + + var result = await context.CloseTransactionAsync(transaction); + if (result == true) + { + response.EC = ERROR_CODE.EC_OK; + } } } } @@ -89,80 +88,82 @@ namespace AuthApi.Services /// /// select user(login) /// - public async Task SelectUser(LoginModel loginModel) + public async Task SelectUser(Login loginModel) { //response - LoginResponseModel response = new LoginResponseModel(); + LoginResponse response = new LoginResponse(); response.EC = ERROR_CODE.EC_USER_LOGIN_FAILED; response.UserID = loginModel.UserID; - //var session = Session.Find(x => x.UserID?.ToLower() == loginModel.UserID?.ToLower()); - //if (session?.AccessTokenExpired < DateTime.Now.ToUnixTime()) - //{ - // Session.Remove(session); - //} + var session = Session.Find(x => x.UserID?.ToLower() == loginModel.UserID?.ToLower()); + if (session?.AccessTokenExpired < DateTime.Now.ToUnixTime()) + { + Session.Remove(session); + } //기존 로그인 체크 - // if (Session.Exists(x => x.UserID == $"{loginModel.UserID?.ToLower()}") == false) + if (Session.Exists(x => x.UserID == $"{loginModel.UserID?.ToLower()}") == false) { if (loginModel != null) { //context using (var scope = _scopeFactory.CreateScope()) { - var context = scope.ServiceProvider.GetRequiredService(); - if (context is not null) - { - try + var provider = scope.ServiceProvider.GetRequiredService(); + using (var context = GetAccountDBContext(provider, 1)) + { + if (context is not null) { - using (var transaction = await context.CreateTransactionAsync(IsolationLevel.ReadUncommitted)) + try { - //select user - var selectUser = await context.TUsers.AsNoTracking().FirstOrDefaultAsync(x => x.CUserId.ToLower() == loginModel!.UserID!.ToLower()); - if (selectUser is not null) + using (var transaction = await context.CreateTransactionAsync(IsolationLevel.ReadUncommitted)) { - if (selectUser.CPasswordHashed == loginModel?.Password) + //select user + var selectUser = await context.TUsers.AsNoTracking().FirstOrDefaultAsync(x => x.CUserId.ToLower() == loginModel!.UserID!.ToLower()); + if (selectUser is not null) { - //select role - var selectRole = await context.TRoles.FindAsync(selectUser.CAuid); - if (selectRole != null) + if (selectUser.CPasswordHashed == loginModel?.Password) { - response.Role = (UserRole)Enum.Parse(typeof(UserRole), selectRole.CRoleId.ToString()); - response.RoleName = selectRole.CRoleName; - } + //select role + var selectRole = await context.TRoles.FindAsync(selectUser.CAuid); + if (selectRole != null) + { + response.Role = (UserRole)Enum.Parse(typeof(UserRole), selectRole.CRoleId.ToString()); + response.RoleName = selectRole.CRoleName; + } - // Session.Add(response); - - if (selectUser.CState == (byte)UserState.Active) - { - response.EC = ERROR_CODE.EC_OK; + Session.Add(response); + if (selectUser.CState == (byte)UserState.Active) + { + response.EC = ERROR_CODE.EC_OK; + } + else if (selectUser.CState == (byte)UserState.Inactive) + { + response.EC = ERROR_CODE.EC_USER_LOGIN_INAVTIVE; + } + else if (selectUser.CState == (byte)UserState.Block) + { + response.EC = ERROR_CODE.EC_USER_LOGIN_BLOCKED; + } } - else if (selectUser.CState == (byte)UserState.Inactive) + else { - response.EC = ERROR_CODE.EC_USER_LOGIN_INAVTIVE; - } - else if (selectUser.CState == (byte)UserState.Block) - { - response.EC = ERROR_CODE.EC_USER_LOGIN_BLOCKED; + response.EC = ERROR_CODE.EC_USER_LOGIN_INVALID_PASSWORD; } } else { - response.EC = ERROR_CODE.EC_USER_LOGIN_INVALID_PASSWORD; + response.EC = ERROR_CODE.EC_USER_LOGIN_NOT_EXIST; + Log4net.WriteLine($"{response.EC}", LogType.Error); } + await context.CloseTransactionAsync(transaction); } - else - { - response.EC = ERROR_CODE.EC_USER_LOGIN_NOT_EXIST; - Log4net.WriteLine($"{response.EC}", LogType.Error); - } - await context.CloseTransactionAsync(transaction); } - } - catch (Exception e) - { - Log4net.WriteLine($"Select User Transaction Error", LogType.Exception); - Log4net.WriteLine(e); + catch (Exception e) + { + Log4net.WriteLine($"Select User Transaction Error", LogType.Exception); + Log4net.WriteLine(e); + } } } } @@ -172,71 +173,74 @@ namespace AuthApi.Services return response; } - public async Task UpdateLoginInfo(LoginModel loginModel, string? RefreshToken = "") + public async Task UpdateLoginInfo(Login loginModel, string? RefreshToken = "") { bool result = false; bool transactionResult = true; using (var scope = _scopeFactory.CreateScope()) { - var context = scope.ServiceProvider.GetRequiredService(); - if (context is not null) + var provider = scope.ServiceProvider.GetRequiredService(); + using (var context = GetAccountDBContext(provider, 1)) { - var selectUser = await context.TUsers.AsNoTracking().FirstOrDefaultAsync(x => x.CUserId.ToLower() == loginModel!.UserID!.ToLower()); - if (selectUser is not null) + if (context is not null) { - using (var transaction = await context.CreateTransactionAsync()) + var selectUser = await context.TUsers.AsNoTracking().FirstOrDefaultAsync(x => x.CUserId.ToLower() == loginModel!.UserID!.ToLower()); + if (selectUser is not null) { - try + using (var transaction = await context.CreateTransactionAsync()) { - //user info - selectUser.CLastLoginDateTime = DateTime.Now; - context.Update(selectUser); - - //refresh token - var findRefreshToken = await context.TRefreshTokens.AsNoTracking().FirstOrDefaultAsync(x => x.CAuid == selectUser.CAuid); - //null이면(없으면) add - if (findRefreshToken == null) + try { - await context.AddAsync(new TRefreshToken + //user info + selectUser.CLastLoginDateTime = DateTime.Now; + context.Update(selectUser); + + //refresh token + var findRefreshToken = await context.TRefreshTokens.AsNoTracking().FirstOrDefaultAsync(x => x.CAuid == selectUser.CAuid); + //null이면(없으면) add + if (findRefreshToken == null) { - CAuid = selectUser.CAuid, - CRefreshToken = $"{RefreshToken}" - }); + await context.AddAsync(new TRefreshToken + { + CAuid = selectUser.CAuid, + CRefreshToken = $"{RefreshToken}" + }); + } + //있으면 update + else + { + findRefreshToken.CRefreshToken = $"{RefreshToken}"; + context.Update(findRefreshToken); + } + + //commit + Log4net.WriteLine(findRefreshToken?.ToJson(), LogType.Debug); + + result = true; } - //있으면 update - else + catch (Exception ex) { - findRefreshToken.CRefreshToken = $"{RefreshToken}"; - context.Update(findRefreshToken); + Log4net.WriteLine(ex); } - //commit - Log4net.WriteLine(findRefreshToken?.ToJson(), LogType.Debug); - - result = true; + transactionResult = await context.CloseTransactionAsync(transaction); } - catch (Exception ex) - { - Log4net.WriteLine(ex); - } - - transactionResult = await context.CloseTransactionAsync(transaction); } - } - else - { - Log4net.WriteLine($"Not Exist User {loginModel.UserID}", LogType.Error); - } + else + { + Log4net.WriteLine($"Not Exist User {loginModel.UserID}", LogType.Error); + } - //db error - if (transactionResult == false) - { - Log4net.WriteLine($"Transaction Error", LogType.Error); - } - else - { - Log4net.WriteLine($"Transaction Success", LogType.DB); + //db error + if (transactionResult == false) + { + Log4net.WriteLine($"Transaction Error", LogType.Error); + } + else + { + Log4net.WriteLine($"Transaction Success", LogType.DB); + } } } } @@ -244,9 +248,9 @@ namespace AuthApi.Services return result; } - public LogoutResponseModel LogoutUser(LogoutModel logoutModel) + public LogoutResponse LogoutUser(Logout logoutModel) { - LogoutResponseModel response = new LogoutResponseModel(); + LogoutResponse response = new LogoutResponse(); response.UserID = logoutModel.UserID; response.EC = ERROR_CODE.EC_USER_LOGOUT_FAILED; @@ -259,5 +263,11 @@ namespace AuthApi.Services return response; } + + private AccountDbContext? GetAccountDBContext(DbContextProvider provider, int dbID) + { + var connectionString = _configService?.GetConfig()?.DataBase?.Find(x => x.DBID == dbID); + return provider?.GetDBContext($"{connectionString?.DBName}"); + } } } diff --git a/Projects/WebApi/WebApi.Library.DBContext/DB/DBContext/AccountDB/Context/AccountDbContext.cs b/Projects/WebApi/WebApi.Library.DBContext/DB/DBContext/AccountDB/Context/AccountDbContext.cs index a053ff5..1775f3d 100644 --- a/Projects/WebApi/WebApi.Library.DBContext/DB/DBContext/AccountDB/Context/AccountDbContext.cs +++ b/Projects/WebApi/WebApi.Library.DBContext/DB/DBContext/AccountDB/Context/AccountDbContext.cs @@ -20,11 +20,7 @@ public partial class AccountDbContext : DbContext public virtual DbSet TRoles { get; set; } - public virtual DbSet TUsers { get; set; } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) -#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263. - => optionsBuilder.UseSqlServer("server=127.0.0.1; user id=SystemX; password=X; database=AccountDB; TrustServerCertificate=true;"); + public virtual DbSet TUsers { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) {