From 081d0952f1f5671944e37fec3bfe8e868c90086b Mon Sep 17 00:00:00 2001 From: "Joanne.Chuang" Date: Tue, 13 Jan 2026 19:40:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B8=AC=E8=A9=A6=E9=A0=81=E9=9D=A2=E5=83=85?= =?UTF-8?q?=E9=99=90=E7=99=BD=E5=90=8D=E5=96=AE=E5=85=A7IP=E4=BD=BF?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Altob.NtuInvoiceGateway.csproj | 4 +- .../Middleware/IpWhitelistMiddleware.cs | 85 ++++++++++++++++++++++ .../Models/InvoiceApiOptions.cs | 6 ++ .../Models/IpWhitelistOptions.cs | 7 ++ Altob.NtuInvoiceGateway/Program.cs | 6 ++ .../appsettings.Production.json | 36 +++++++++ Altob.NtuInvoiceGateway/appsettings.Staging.json | 36 +++++++++ Altob.NtuInvoiceGateway/appsettings.json | 4 + 8 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 Altob.NtuInvoiceGateway/Middleware/IpWhitelistMiddleware.cs create mode 100644 Altob.NtuInvoiceGateway/Models/InvoiceApiOptions.cs create mode 100644 Altob.NtuInvoiceGateway/Models/IpWhitelistOptions.cs create mode 100644 Altob.NtuInvoiceGateway/appsettings.Production.json create mode 100644 Altob.NtuInvoiceGateway/appsettings.Staging.json diff --git a/Altob.NtuInvoiceGateway/Altob.NtuInvoiceGateway.csproj b/Altob.NtuInvoiceGateway/Altob.NtuInvoiceGateway.csproj index 66267b6..d0906e6 100644 --- a/Altob.NtuInvoiceGateway/Altob.NtuInvoiceGateway.csproj +++ b/Altob.NtuInvoiceGateway/Altob.NtuInvoiceGateway.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/Altob.NtuInvoiceGateway/Middleware/IpWhitelistMiddleware.cs b/Altob.NtuInvoiceGateway/Middleware/IpWhitelistMiddleware.cs new file mode 100644 index 0000000..8ce6108 --- /dev/null +++ b/Altob.NtuInvoiceGateway/Middleware/IpWhitelistMiddleware.cs @@ -0,0 +1,85 @@ +using System.Net; +using Altob.NtuInvoiceGateway.Models; +using Microsoft.Extensions.Options; + +namespace Altob.NtuInvoiceGateway.Middleware; + +public class IpWhitelistMiddleware +{ + private readonly RequestDelegate _next; + private readonly ILogger _logger; + private readonly IpWhitelistOptions _ipWhitelistOptions; + + public IpWhitelistMiddleware( + RequestDelegate next, + ILogger logger, + IOptions ipWhitelistOptions) + { + _next = next; + _logger = logger; + _ipWhitelistOptions = ipWhitelistOptions.Value; + } + + public async Task InvokeAsync(HttpContext context) + { + // 如果 IP 白名單未啟用,直接通過 + if (!_ipWhitelistOptions.Enabled) + { + await _next(context); + return; + } + + // 只對 Test 開頭的頁面進行 IP 白名單檢查 + var path = context.Request.Path.Value ?? string.Empty; + if (!path.StartsWith("/Test", StringComparison.OrdinalIgnoreCase)) + { + await _next(context); + return; + } + + var remoteIp = context.Connection.RemoteIpAddress; + var clientIp = GetClientIp(context); + + _logger.LogDebug("IP Whitelist check for Test page - Path: {Path}, RemoteIp: {RemoteIp}, ClientIp: {ClientIp}", + path, remoteIp, clientIp); + + // 檢查 RemoteIp 或 ClientIp 是否在白名單中 + var isAllowed = false; + + if (remoteIp != null && IsIpAllowed(remoteIp.ToString())) + { + isAllowed = true; + } + else if (clientIp != null && IsIpAllowed(clientIp)) + { + isAllowed = true; + } + + if (!isAllowed) + { + _logger.LogWarning("{ServiceName} - {ActionName} - {CallerIp} - {ClientIp} - IP access denied to Test page: {Path}", + "NtuInvoiceGateway", "IpWhitelistCheck", remoteIp?.ToString() ?? "Unknown", clientIp, path); + + context.Response.StatusCode = StatusCodes.Status403Forbidden; + await context.Response.WriteAsync("Access denied. Your IP address is not allowed to access test pages."); + return; + } + + await _next(context); + } + + private bool IsIpAllowed(string ipAddress) + { + return _ipWhitelistOptions.AllowedIps.Contains(ipAddress); + } + + private string? GetClientIp(HttpContext context) + { + var forwardedFor = context.Request.Headers["X-Forwarded-For"].FirstOrDefault(); + if (!string.IsNullOrEmpty(forwardedFor)) + { + return forwardedFor.Split(',').FirstOrDefault()?.Trim(); + } + return null; + } +} diff --git a/Altob.NtuInvoiceGateway/Models/InvoiceApiOptions.cs b/Altob.NtuInvoiceGateway/Models/InvoiceApiOptions.cs new file mode 100644 index 0000000..0196b84 --- /dev/null +++ b/Altob.NtuInvoiceGateway/Models/InvoiceApiOptions.cs @@ -0,0 +1,6 @@ +namespace Altob.NtuInvoiceGateway.Models; + +public class InvoiceApiOptions +{ + public string Endpoint { get; set; } = string.Empty; +} diff --git a/Altob.NtuInvoiceGateway/Models/IpWhitelistOptions.cs b/Altob.NtuInvoiceGateway/Models/IpWhitelistOptions.cs new file mode 100644 index 0000000..9210ee0 --- /dev/null +++ b/Altob.NtuInvoiceGateway/Models/IpWhitelistOptions.cs @@ -0,0 +1,7 @@ +namespace Altob.NtuInvoiceGateway.Models; + +public class IpWhitelistOptions +{ + public bool Enabled { get; set; } + public List AllowedIps { get; set; } = new(); +} diff --git a/Altob.NtuInvoiceGateway/Program.cs b/Altob.NtuInvoiceGateway/Program.cs index 9a24c4c..d30f6f9 100644 --- a/Altob.NtuInvoiceGateway/Program.cs +++ b/Altob.NtuInvoiceGateway/Program.cs @@ -1,3 +1,5 @@ +using Altob.NtuInvoiceGateway.Middleware; +using Altob.NtuInvoiceGateway.Models; using Serilog; var builder = WebApplication.CreateBuilder(args); @@ -19,6 +21,8 @@ builder.Services.Configure( builder.Configuration.GetSection("CompanyInfo")); builder.Services.Configure( builder.Configuration.GetSection("InvoiceApi")); +builder.Services.Configure( + builder.Configuration.GetSection("IpWhitelist")); var app = builder.Build(); @@ -36,6 +40,8 @@ try app.UseRouting(); + app.UseMiddleware(); + app.UseAuthorization(); app.MapStaticAssets(); diff --git a/Altob.NtuInvoiceGateway/appsettings.Production.json b/Altob.NtuInvoiceGateway/appsettings.Production.json new file mode 100644 index 0000000..bec1f9b --- /dev/null +++ b/Altob.NtuInvoiceGateway/appsettings.Production.json @@ -0,0 +1,36 @@ +{ + "DetailedErrors": false, + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Error" + } + }, + "Serilog": { + "Using": [ "Serilog.Sinks.Seq" ], + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "System": "Warning" + } + }, + "WriteTo": [ + { + "Name": "Seq", + "Args": { + "serverUrl": "http://localhost:5341", + "apiKey": "" + } + } + ], + "Enrich": [ "FromLogContext" ] + }, + "IpWhitelist": { + "Enabled": true, + "AllowedIps": [ "127.0.0.1", "::1","61.219.172.82","61.222.237.97","125.227.77.79" ] + }, + "InvoiceApi": { + "Endpoint": "http://140.112.143.162:22055/api/Intella/invoiceInfo" + } +} diff --git a/Altob.NtuInvoiceGateway/appsettings.Staging.json b/Altob.NtuInvoiceGateway/appsettings.Staging.json new file mode 100644 index 0000000..a909118 --- /dev/null +++ b/Altob.NtuInvoiceGateway/appsettings.Staging.json @@ -0,0 +1,36 @@ +{ + "DetailedErrors": false, + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Error" + } + }, + "Serilog": { + "Using": [ "Serilog.Sinks.Seq" ], + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "System": "Warning" + } + }, + "WriteTo": [ + { + "Name": "Seq", + "Args": { + "serverUrl": "http://localhost:5341", + "apiKey": "" + } + } + ], + "Enrich": [ "FromLogContext" ] + }, + "IpWhitelist": { + "Enabled": true, + "AllowedIps": [ "127.0.0.1", "::1" ] + }, + "InvoiceApi": { + "Endpoint": "http://192.168.110.72:22055/api/Intella/invoiceInfo" + } +} diff --git a/Altob.NtuInvoiceGateway/appsettings.json b/Altob.NtuInvoiceGateway/appsettings.json index 730ffa3..fd9c777 100644 --- a/Altob.NtuInvoiceGateway/appsettings.json +++ b/Altob.NtuInvoiceGateway/appsettings.json @@ -25,6 +25,10 @@ ], "Enrich": [ "FromLogContext" ] }, + "IpWhitelist": { + "Enabled": true, + "AllowedIps": [ "127.0.0.1", "::1" ] + }, "AllowedHosts": "*", "CompanyInfo": { "Name": "國立台灣大學臨時停車場",