ServiceCollectionExtensions.cs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. using SharedKernel;
  2. using Infrastructure.Persistence;
  3. using Infrastructure.Persistence.Identity;
  4. using Infrastructure.Extensions;
  5. using Microsoft.AspNetCore.Authentication.Cookies;
  6. using Microsoft.AspNetCore.HttpOverrides;
  7. using Microsoft.AspNetCore.Identity;
  8. using Microsoft.Extensions.Caching.Distributed;
  9. using System.Net;
  10. using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
  11. namespace Admin.Extensions
  12. {
  13. public static class ServiceCollectionExtensions
  14. {
  15. public static void AddAdminForwardedHeaders(this IServiceCollection services, AppSettings settings)
  16. {
  17. services.Configure<ForwardedHeadersOptions>(options =>
  18. {
  19. options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost;
  20. options.ForwardLimit = settings.ForwardedHeaders.ForwardLimit <= 0 ? 1 : settings.ForwardedHeaders.ForwardLimit;
  21. // 설정에서 KnownProxies / KnownNetworks 읽기
  22. var knownProxies = settings.ForwardedHeaders.KnownProxies;
  23. if (knownProxies != null)
  24. {
  25. foreach (var proxy in knownProxies)
  26. {
  27. if (IPAddress.TryParse(proxy, out var ip))
  28. {
  29. options.KnownProxies.Add(ip);
  30. }
  31. }
  32. }
  33. var knownNetworks = settings.ForwardedHeaders.KnownNetworks;
  34. if (knownNetworks != null)
  35. {
  36. foreach (var net in knownNetworks)
  37. {
  38. var parts = net.Split('/');
  39. if (parts.Length == 2 && IPAddress.TryParse(parts[0], out var networkIp) && int.TryParse(parts[1], out var prefix))
  40. {
  41. options.KnownNetworks.Add(new IPNetwork(networkIp, prefix));
  42. }
  43. }
  44. }
  45. });
  46. }
  47. // 관리자단 비밀번호 정책 구성
  48. public static void AddAdminIdentity(this IServiceCollection services, AppSettings settings)
  49. {
  50. services.AddIdentity<ApplicationUser, IdentityRole>(options =>
  51. {
  52. options.SignIn.RequireConfirmedAccount = true; // 이메일 확인 활성화
  53. // Password settings.
  54. options.Password.RequireDigit = true;
  55. options.Password.RequireLowercase = true;
  56. options.Password.RequireNonAlphanumeric = true;
  57. options.Password.RequireUppercase = true;
  58. options.Password.RequiredLength = 6;
  59. options.Password.RequiredUniqueChars = 1;
  60. // Lockout settings.
  61. options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
  62. options.Lockout.MaxFailedAccessAttempts = 5;
  63. options.Lockout.AllowedForNewUsers = true;
  64. // User settings.
  65. options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
  66. options.User.RequireUniqueEmail = true;
  67. })
  68. .AddDefaultUI() // 기본 UI 사용
  69. .AddDefaultTokenProviders() // 기본 토큰 제공자 사용
  70. .AddEntityFrameworkStores<IdentityDbContext>(); // 사용자 계정 저장소
  71. // Identity Cookie Session Store 설정
  72. services.AddSingleton<ITicketStore>(sp =>
  73. new DistributedCacheTicketStore(
  74. sp.GetRequiredService<IDistributedCache>(),
  75. keyPrefix: settings.Redis.AuthTicketPrefix
  76. )
  77. );
  78. services.AddOptions<CookieAuthenticationOptions>(IdentityConstants.ApplicationScheme).Configure<ITicketStore>((options, ticketStore) =>
  79. {
  80. options.SessionStore = ticketStore;
  81. });
  82. services.ConfigureApplicationCookie(options =>
  83. {
  84. // Cookie settings
  85. options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
  86. options.LoginPath = "/Identity/Account/Login";
  87. options.AccessDeniedPath = "/Identity/Account/AccessDenied";
  88. options.SlidingExpiration = true;
  89. options.Events.OnRedirectToLogin = context =>
  90. {
  91. if (context.Request.Path.StartsWithSegments("/Api"))
  92. {
  93. context.Response.StatusCode = StatusCodes.Status401Unauthorized;
  94. return Task.CompletedTask;
  95. }
  96. context.Response.Redirect(context.RedirectUri); // 기존 쿠키 인증 흐름 유지
  97. return Task.CompletedTask;
  98. };
  99. options.Events.OnRedirectToAccessDenied = context =>
  100. {
  101. if (context.Request.Path.StartsWithSegments("/Api"))
  102. {
  103. context.Response.StatusCode = StatusCodes.Status403Forbidden;
  104. return Task.CompletedTask;
  105. }
  106. context.Response.Redirect(context.RedirectUri);
  107. return Task.CompletedTask;
  108. };
  109. });
  110. }
  111. }
  112. }