一.前言

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
[HttpGet]
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
[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-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;
}
}

我们再次访问:

可以看到我们的真实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

官方解决方案

官方对于这个问题,有封装中间件,可以直接使用,详情请点击 点我

ASP.NET Core 搭配 Nginx 的真实IP问题的更多相关文章

  1. ASP.NET获取用户端的真实IP

    ASP.NET获取用户端的真实IP在各种场景都能用到,但是用户网端变幻莫测情况众多,获取真实IP还真是不容易啊.下面分享个比较好一点的方法: 获取IP初始版本 /// <summary> ...

  2. nodejs+nginx获取真实ip

    nodejs + nginx获取真实ip分为两部分: 第一.配置nginx: 第二.通过nodejs代码获取: 其他语言也是一样的,都是配置nginx之后,在http头里面获取“x-forwarded ...

  3. ASP.NET Core 实战:使用 Docker 容器化部署 ASP.NET Core + MySQL + Nginx

    一.前言 在之前的文章(ASP.NET Core 实战:Linux 小白的 .NET Core 部署之路)中,我介绍了如何在 Linux 环境中安装 .NET Core SDK / .NET Core ...

  4. docker部署Asp.Net Core、Nginx、MySQL

    2019/10/24,docker19.03.4, .netcore 3.0,CentOS7.6 摘要:asp.net core 3.0 网站项目容器化部署,使用docker-compose编排Ngi ...

  5. Nginx 获取真实 IP 方案

    问题根源: 基于七层的负载均衡系统,获取IP的原理都是通过XRI和XFF进行处理,从中选出“正常情况下”的源头IP,然而这两个Header都是普通的HTTP头,任何代理程序都可以轻易修改伪造它们,使得 ...

  6. ubuntu下发布asp.net core并用nginx代理之旅

    asp.net core 1.0.1发布已有些日子了,怀着好奇的心情体验了把ubuntu下的asp.net core 系统运行环境:ubuntu 16.0.4 for developer 首先搭建.n ...

  7. asp.net core 二 Nginx Supervisor 负载,监听

           ASP.NET Core负载均衡集群搭建(CentOS7+Nginx+Supervisor+Kestrel)          asp.net core在linux运行下,一但命令行退出 ...

  8. 发布Asp.net core到nginx 使用nginx代理

    In this guide, we will cover setting up a production-ready ASP.NET environment on an Ubuntu 16.04 Se ...

  9. 首层nginx 传递 二级代理,三级代理......多级代理nginx 客户端真实IP的方法

    首层nginx(172.25.10.1):先获取真实IP($remote_addr),再将真实IP传递给X-Forwarded-For    proxy_set_header X-Real-IP $r ...

随机推荐

  1. C#实现将Chart图表生成JPG图片的方法

    SaveFileDialog savefile= new SaveFileDialog();            savefile.Filter = "JPEG文件|*.jpg" ...

  2. Java数据结构和算法 - 堆

    堆的介绍 Q: 什么是堆? A: 这里的“堆”是指一种特殊的二叉树,不要和Java.C/C++等编程语言里的“堆”混淆,后者指的是程序员用new能得到的计算机内存的可用部分 A: 堆是有如下特点的二叉 ...

  3. javascript权威指南笔记[6-8]

    1.三类对象,两类属性 2.原型链只有在查询的时候才会体现 3.对象属性访问 4.属性赋值 5.delete只是断开属性和宿主对象的关系,不会去操作属性中的属性 6.Object.ke() var m ...

  4. ASP.NET MVC权限控制思路

    在系统开发的时候一个老生常谈的权限管理问题,翻阅了很多的网络资料,但是总感觉离实际使用还有一段距离,其实权限控制无非就几个“请求.页面按钮.字段显示”, 对于前端权限就需要配合JS了, 这里主要展示我 ...

  5. lua-nginx-module模块里ngx_lua的所有指令以及可用ngx所有方法

    http://www.04007.cn/article/430.html

  6. 《深入理解Java虚拟机》-----第3章 垃圾收集器与内存分配策略

    Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的“高墙”,墙外面的人想进去,墙里面的人却想出来. 3.1 概述 说起垃圾收集(Garbage Collection,GC),大部分人都把这 ...

  7. 从零开始学习PYTHON3讲义(十)自己做一个“电子记事本”

    <从零开始PYTHON3>第十讲 截至上一讲,我们已经完成了Python语言的基本部分.我们用了三讲来讨论Python语言的控制结构,用了两讲来介绍Python的基本数据类型.可以说仅就语 ...

  8. 【Python3爬虫】大众点评爬虫(破解CSS反爬)

    本次爬虫的爬取目标是大众点评上的一些店铺的店铺名称.推荐菜和评分信息. 一.页面分析 进入大众点评,然后选择美食(http://www.dianping.com/wuhan/ch10),可以看到一页有 ...

  9. 一个box-sizing: border-box和felx混合使用中遇到的问题

    之前在项目中遇到一个布局上很趣的问题(也可能是笔者才疏学浅,哈哈).布局大概是这样的: 外层包裹器:采用flex布局,并指定内部子弹性盒子元素水平显示 侧边栏:flex盒子的子元素,可收起和展开.展开 ...

  10. 搞懂Redis到底快在哪里

    前言 Redis是一种基于键值对(Key-Value)的NoSQL数据库,Redis的Value可以由String,hash,list,set,zset,Bitmaps,HyperLogLog等多种数 ...