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": "國立台灣大學臨時停車場",