ServiceCollectionExtensions.cs 4.8 KB

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