ASP.NET Core 搭配 Nginx 的真實IP問題

一.前言

img

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

二.發現問題

1.安裝 Nginx

這裏我就選用我安裝在 CentOS 7.2 上的 Nginx,在 CentOS 安裝 Nginx 的同窗能夠參考我之前寫的文章:CentOS 7 源碼編譯安裝 Nginxnginx

2.新建 ASP.NET Core 項目

第一步:git

1541941721350

第二步:github

1541941745624

3.編寫代碼

編輯 ValuesControllershell

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}");
        }

編輯 Startupapi

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

經過瀏覽器訪問驗證:

1541946415816

但是卻獲取到了 127.0.0.1,這是由於 們的請求到了 Nginx,而後 Nginx 再將咱們的請求轉發到 ASP.NET Core 應用程序,實際上與 ASP.NET Core 應用程序 創建鏈接的是 Nginx ,因此獲取到了服務器本地 IP (Nginx和程序部署在一臺機子上)。請求流程以下圖:

1541984724645

三.解決問題

修改程序代碼以便顯示更詳細的信息:

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;
    }
}

再次訪問:

1541948087185

能夠看到X-Real-IPX-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;
    }
}

咱們再次訪問:

1541949268726

能夠看到咱們的真實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。請求流程以下圖:

1541985228889

五.如何在代碼裏最小改動

通過上面的講解,顯而易見咱們在代碼裏沒法直接經過 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中配置

1541950121051

應放在最靠前的位置,以避免有中間件獲取到了未重置的IP地址。

保持前面的模擬cdn第二中狀況架構,再次進行測試:

1541950299862

能夠看到經過 RemoteIpAddress 獲取到了真實ip。這種解決方案算是比較好的了。

這裏提一下 Nginx RealIP Module 是 Nginx 獲取真實ip的一個模塊,有興趣的同窗能夠本身去研究一下。

六.使用組件 Unicorn.AspNetCore

Unicorn.AspNetCore 裏面我有封裝處理ip的中間件。

經過nuget安裝:

Install-Package Unicorn.AspNetCore

而後在 Program 中添加:

1542003717012

開源地址:https://github.com/UCPlan/Unicorn/tree/master/src/Infrastructure/Unicorn.AspNetCore/Middleware/RealIp

相關文章
相關標籤/搜索