Nginx(Engine X)是一個高性能HTTP和反向代理服務,是由俄羅斯人伊戈爾·賽索耶夫爲訪問量第二的Rambler.ru站點(俄文:Рамблер)開發的,第一個公開版本0.1.0發佈於2004年10月4日。 若是你是一名 ASP.NET Core 開發人員,而且你的 ASP.NET Core 應用部署在Linux上,相信你應該或多或少與 Nginx 有過接觸,在咱們將 ASP.NET Core 部署在 Linux 上時,它是被用作反向代理的最好選擇之一。今天和你們聊一聊當咱們使用了 Nginx 反向代理後,咱們程序中獲取真實IP(客戶端真實ip,本文簡稱「真實IP」)的問題。html
這裏我就選用我安裝在 CentOS 7.2 上的 Nginx,在 CentOS 安裝 Nginx 的同窗能夠參考我之前寫的文章:CentOS 7 源碼編譯安裝 Nginxnginx
第一步:git
第二步:github
編輯 ValuesController
shell
private readonly HttpContext _context; public ValuesController(IHttpContextAccessor accessor) { _context = accessor.HttpContext; } // GET api/values [HttpGet] public ActionResult<IEnumerable<string>> Get() { return Ok($"獲取到的真實IP:{_context.Connection.RemoteIpAddress}"); }
編輯 Startup
api
public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); }
本文略此步瀏覽器
新建配置文件 realiptest.conf
緩存
server { listen 5002; access_log off; location / { proxy_pass http://localhost:5000; } }
服務器地址:192.168.157.132服務器
我本機地址:192.168.157.1架構
那麼我本機經過訪問 http://192.168.157.132:5002/api/values api獲取到的ip地址應該是我本機的,即 192.168.157.1
經過瀏覽器訪問驗證:
但是卻獲取到了 127.0.0.1,這是由於 們的請求到了 Nginx,而後 Nginx 再將咱們的請求轉發到 ASP.NET Core 應用程序,實際上與 ASP.NET Core 應用程序 創建鏈接的是 Nginx ,因此獲取到了服務器本地 IP (Nginx和程序部署在一臺機子上)。請求流程以下圖:
修改程序代碼以便顯示更詳細的信息:
ValuesController
// GET api/values [HttpGet] public ActionResult<IEnumerable<string>> Get() { StringBuilder sb=new StringBuilder(); sb.AppendLine($"RemoteIpAddress:{_context.Connection.RemoteIpAddress}"); if (Request.Headers.ContainsKey("X-Real-IP")) { sb.AppendLine($"X-Real-IP:{Request.Headers["X-Real-IP"].ToString()}"); } if (Request.Headers.ContainsKey("X-Forwarded-For")) { sb.AppendLine($"X-Forwarded-For:{Request.Headers["X-Forwarded-For"].ToString()}"); } return Ok(sb.ToString()); }
修改反向代理配置:
server { listen 5002; access_log off; location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://localhost:5000; } }
再次訪問:
能夠看到X-Real-IP
和 X-Forwarded-For
請求頭獲取到了真實IP,咱們經過修改 Nginx 配置,讓程序接收到的請求信息攜帶真實IP。Nginx 經過在 X-Real-IP 、X-Forwarded-For 請求頭設置了與它鏈接的遠程ip。
以上解決辦法對於沒有使用CDN是適用的。
咱們的請求通過一個或者多個cdn結點之後,咱們的程序如何獲取真實IP呢,這就要看cdn服務商提供的解決辦法了,通常有兩種:
1.cdn服務商支持設置真實ip到某個指定的請求頭,這樣咱們經過這個請求頭就能獲取了 。
2.通常通過cdn都會把真實ip通過的結點ip信息添加到頭 X-Forwarded-For
,咱們取這個頭裏的第一個ip就是真實ip。
添加 nginx 配置,讓他再次代理 5002 端口(前面添加的代理ASP.NET Core 程序),模擬cdn第二種方案:
server { listen 5003; access_log off; location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://192.168.157.132:5002; } }
咱們再次訪問:
能夠看到咱們的真實ip被放到 X-Forwarded-For
請求頭的第一個IP,X-Real-IP 獲取到的是上一層代理的ip。
X-Forwarded-For 來自百度百科的解釋:X-Forwarded-For 簡稱XFF頭,它表明客戶端,也就是HTTP的請求端真實的IP,只有在經過了HTTP 代理或者負載均衡服務器時纔會添加該項。它不是RFC中定義的標準請求頭信息,在squid緩存代理服務器開發文檔中能夠找到該項的詳細介紹。標準格式以下:X-Forwarded-For: client1, proxy1, proxy2。請求流程以下圖:
通過上面的講解,顯而易見咱們在代碼裏沒法直接經過 RemoteIpAddress
獲取真實ip,那麼若是咱們在編寫代碼時,不少地方直接採用 RemoteIpAddress
獲取真實ip怎麼辦,難道須要修改每一處嗎,這裏分享一個簡單的解決辦法,就是利用 ASP.NET Core 中間件給 RemoteIpAddress 從新賦值。
編寫 RealIpMiddleware 中間件:
public class RealIpMiddleware { private readonly RequestDelegate _next; public RealIpMiddleware(RequestDelegate next) { _next = next; } public Task Invoke(HttpContext context) { var headers = context.Request.Headers; if (headers.ContainsKey("X-Forwarded-For")) { context.Connection.RemoteIpAddress=IPAddress.Parse(headers["X-Forwarded-For"].ToString().Split(',', StringSplitOptions.RemoveEmptyEntries)[0]); } return _next(context); } }
若是是前面提到的cdn的第一種狀況,只需判斷cdn服務商提供的特殊請求頭就好了。
在Startup中配置
應放在最靠前的位置,以避免有中間件獲取到了未重置的IP地址。
保持前面的模擬cdn第二中狀況架構,再次進行測試:
能夠看到經過 RemoteIpAddress
獲取到了真實ip。這種解決方案算是比較好的了。
這裏提一下 Nginx RealIP Module 是 Nginx 獲取真實ip的一個模塊,有興趣的同窗能夠本身去研究一下。
Unicorn.AspNetCore
裏面我有封裝處理ip的中間件。
經過nuget安裝:
Install-Package Unicorn.AspNetCore
而後在 Program 中添加: