ASP.NET Core 搭配 Nginx 的真实IP问题
一.前言
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”)的問題。
二.發現問題
1.安裝 Nginx
這里我就選用我安裝在 CentOS 7.2 上的 Nginx,在 CentOS 安裝 Nginx 的同學可以參考我以前寫的文章:CentOS 7 源碼編譯安裝?Nginx
2.新建 ASP.NET Core 項目
第一步:
第二步:
3.編寫代碼
編輯?ValuesController
? ? ? ?private readonly HttpContext _context; ? ? ? ?public ValuesController(IHttpContextAccessor accessor) ? ? ? ?{_context = accessor.HttpContext;} ? ? ? ?// GET api/values[] ? ? ? ?public ActionResult<IEnumerable<string>> Get(){ ? ? ? ? ? ?return Ok($"獲取到的真實IP:{_context.Connection.RemoteIpAddress}");}編輯?Startup
? ? ? ?public void ConfigureServices(IServiceCollection services) ? ? ? ?{services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();}4.測試
(1)將程序部署到服務器
本文略此步
(2)配置 Nginx 反向代理
新建配置文件?realiptest.conf
server { ? ?listen 5002; ? ?access_log ?off; ? ?location / { ? ? ? proxy_pass http://localhost:5000; } }(3)測試訪問
服務器地址: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[] ? ? ?? ? ? ?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如何解決
我們的請求經過一個或者多個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
Unicorn.AspNetCore?里面我有封裝處理ip的中間件。
通過nuget安裝:
Install-Package Unicorn.AspNetCore然后在 Program 中添加:
開源地址:https://github.com/UCPlan/Unicorn/tree/master/src/Infrastructure/Unicorn.AspNetCore/Middleware/RealIp
原文地址:https://www.cnblogs.com/stulzq/p/9946262.html
.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總 http://www.csharpkit.com
總結
以上是生活随笔為你收集整理的ASP.NET Core 搭配 Nginx 的真实IP问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [翻译] 使用 Visual Studi
- 下一篇: 重磅!!!微软发布.NET Core 2