在 Web 應用中獲取用戶真實 IP 是日誌審計、風控限流、地域業務的基礎。由於反向代理 (Nginx、IIS ARR、CDN) 的存在,Request.UserHostAddress 往往拿到的是代理服務器 IP。本文提供一個静态方法 GetIP,按優先級讀取標準頭,並附帶 IPv4 正則驗證方法 IsIP。
using System;
using System.Text.RegularExpressions;
using System.Web;
public static class IPHelper {
///
/// 獲取客戶端真實 IP (支持反向代理)
///
public static string GetIP() {
HttpRequest request = HttpContext.Current.Request;
string ip = request.ServerVariables["HTTP_X_FORWARDED_FOR"]; // 標準代理頭
if (string.IsNullOrEmpty(ip))
ip = request.ServerVariables["REMOTE_ADDR"]; // 直連或非標準代理
if (string.IsNullOrEmpty(ip))
ip = request.UserHostAddress; // 兜底
// 驗證合法性,非法返回 0.0.0.0
return (string.IsNullOrEmpty(ip) || !IsIP(ip)) ? "0.0.0.0" : ip;
}
///
/// 正則驗證 IPv4 格式 (0.0.0.0 ~ 255.255.255.255)
///
public static bool IsIP(string ip) {
// 正則解析:每段 0-255,共 4 段
string pattern = @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$";
return Regex.IsMatch(ip, pattern);
}
}
| 優先級 | 來源 | 適用場景 |
|---|---|---|
| 1 | HTTP_X_FORWARDED_FOR | Nginx/Apache/CDN 等標準代理,格式為 client, proxy1, proxy2,取第一個。 |
| 2 | REMOTE_ADDR | 無代理直連、或代理未轉發 XFF 時,Web 服務器直接看到的客戶端 IP。 |
| 3 | UserHostAddress | ASP.NET 封裝屬性,本質同 REMOTE_ADDR,作為最後兜底。 |
注意:X-Forwarded-For 可被偽造,安全敏感場景 (登錄、支付) 需結合 WAF、信任代理 IP 白名單、或讀取 X-Real-IP (需代理配置)。
^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$
2[0-4]\d:200-24925[0-5]:250-255[01]?\d\d?:0-199 (含 1-2 位數,允許前導 0)\.:字面點號,{3} 重複 3 次,最後一段無點號// 注入 IHttpContextAccessor
public class IPHelper {
private readonly IHttpContextAccessor _http;
public IPHelper(IHttpContextAccessor http) => _http = http;
public string GetIP() {
var request = _http.HttpContext?.Request;
if (request == null) return "0.0.0.0";
string ip = request.Headers["X-Forwarded-For"].FirstOrDefault();
if (string.IsNullOrEmpty(ip))
ip = request.HttpContext.Connection.RemoteIpAddress?.ToString();
return IsIP(ip) ? ip : "0.0.0.0";
}
// IsIP 同前
}
註冊服務:builder.Services.AddHttpContextAccessor(); builder.Services.AddSingleton
// 全局異常日誌記錄 IP
logger.Error($"用戶 {IPHelper.GetIP()} 觸發異常: {ex.Message}");
// 登錄限流 Key
string limitKey = $"login_limit_{IPHelper.GetIP()}";
cache.Increment(limitKey, TimeSpan.FromMinutes(15));
上述正則僅支持 IPv4。現代雲環境常見 IPv6 (如 ::1、2409:8a00::123),建議改用 IPAddress.TryParse 並檢查 AddressFamily:
public static bool IsValidIP(string ip) {
return IPAddress.TryParse(ip, out var addr) &&
(addr.AddressFamily == AddressFamily.InterNetwork || // IPv4
addr.AddressFamily == AddressFamily.InterNetworkV6); // IPv6
}
將 IPHelper 納入公共庫,並在中間件/過濾器中統一調用,確保全站 IP 獲取邏輯一致、可審計。