一、前言

前文 Nginx 解决WebApi跨域二次请求以及Vue单页面问题 当中虽然解决了跨域问题带来的二次请求,但也产生了一个新的问题,就是如果需要获取用户IP的时候,获取的IP地址总是本机地址。

二、原因

由于Nginx反向代理后,在应用中取得的IP都是反向代理服务器的IP,取得的域名也是反向代理配置的Url的域名。

三、解决方案

解决该问题,需要在Nginx反向代理配置中添加一些配置信息,目的将客户端的真实IP和域名传递到应用程序中。同时,也要修改获取IP地址的方法。

但是需要注意的是,通过Nginx反向代理后,如果访问IP通过了几层代理,可能取得的IP地址是这种格式:clientIP,proxy1,proxy2

如果需要将IP地址插入到数据库的话,需要做防止注入。因此要对上述的IP地址的格式进行截取。

3.1 Nginx 配置如下

    server {
listen 9461; # 监听端口号
server_name localhost 192.168.88.22; # 访问地址
location / {
root 项目路径; # 例如:E:/Publish/xxx/;
index index.html; # 此处用于处理 Vue、Angular、React 使用H5 的 History时 重写的问题
if (!-e $request_filename) {
rewrite ^(.*) /index.html last;
break;
}
} # 代理服务端接口
location /api {
proxy_pass http://localhost:9460/api;# 代理接口地址
# Host配置以及域名传递
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

3.2 C#代码获取真实IP方法

#region Ip(客户端IP地址)

/// <summary>
/// 客户端IP地址
/// </summary>
public static string Ip
{
get
{
var result = string.Empty;
if (HttpContext.Current != null)
{
result = GetWebClientIp();
}
if (string.IsNullOrWhiteSpace(result))
{
result = GetLanIp();
}
return result;
}
} /// <summary>
/// 获取Web客户端的IP
/// </summary>
/// <returns></returns>
private static string GetWebClientIp()
{
var ip = GetWebProxyRealIp() ?? GetWebRemoteIp();
foreach (var hostAddress in Dns.GetHostAddresses(ip))
{
if (hostAddress.AddressFamily == AddressFamily.InterNetwork)
{
return hostAddress.ToString();
}
}
return string.Empty;
} /// <summary>
/// 获取Web远程IP
/// </summary>
/// <returns></returns>
private static string GetWebRemoteIp()
{
try
{
return HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"] ??
HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"] ?? "";
}
catch (Exception e)
{
return string.Empty;
}
} /// <summary>
/// 获取Web代理真实IP
/// </summary>
/// <returns></returns>
private static string GetWebProxyRealIp()
{
var request = HttpContext.Current.Request;
string ip = request.Headers.Get("x-forwarded-for"); if (string.IsNullOrEmpty(ip) || string.Equals("unknown", ip, StringComparison.OrdinalIgnoreCase))
{
ip = request.Headers.Get("Proxy-Client-IP");
} if (string.IsNullOrEmpty(ip) || string.Equals("unknown", ip, StringComparison.OrdinalIgnoreCase))
{
ip = request.Headers.Get("WL-Proxy-Client-IP");
} if (string.IsNullOrEmpty(ip) || string.Equals("unknown", ip, StringComparison.OrdinalIgnoreCase))
{
ip = request.UserHostAddress;
} if (string.IsNullOrEmpty(ip))
{
return string.Empty;
}
// 可能存在如下格式:X-Forwarded-For: client, proxy1, proxy2
if (ip.Contains(", "))
{
// 如果存在多个反向代理,获得的IP是一个用逗号分隔的IP集合,取第一个
// X-Forwarded-For: client 第一个
string[] ips = ip.Split(new string[1] {", "}, StringSplitOptions.RemoveEmptyEntries);
var i = 0;
for (i = 0; i < ips.Length; i++)
{
if (ips[i] != "")
{
// 判断是否为内网IP
if (false == IsInnerIp(ips[i]))
{
IPAddress realIp;
if (IPAddress.TryParse(ips[i], out realIp) && ips[i].Split('.').Length == 4)
{
//合法IP
return ips[i];
} return "";
}
}
} ip = ips[0];// 默认获取第一个IP地址
} return ip; } /// <summary>
/// 判断IP地址是否为内网IP地址
/// </summary>
/// <param name="ip">IP地址</param>
/// <returns></returns>
private static bool IsInnerIp(string ip)
{
bool isInnerIp = false;
ulong ipNum = Ip2Ulong(ip); /**
* 私有IP
* A类:10.0.0.0-10.255.255.255
* B类:172.16.0.0-172.31.255.255
* C类:192.168.0.0-192.168.255.255
* 当然,还有127这个网段是环回地址
*/ ulong aBegin = Ip2Ulong("10.0.0.0");
ulong aEnd = Ip2Ulong("10.255.255.255");
ulong bBegin = Ip2Ulong("172.16.0.0");
ulong bEnd = Ip2Ulong("10.31.255.255");
ulong cBegin = Ip2Ulong("192.168.0.0");
ulong cEnd = Ip2Ulong("192.168.255.255"); isInnerIp = IsInner(ipNum, aBegin, aEnd) || IsInner(ipNum, bBegin, bEnd) || IsInner(ipNum, cBegin, cEnd) ||
ip.Equals("127.0.0.1");
return isInnerIp;
} /// <summary>
/// 将IP地址转换为Long型数字
/// </summary>
/// <param name="ip">IP地址</param>
/// <returns></returns>
private static ulong Ip2Ulong(string ip)
{
byte[] bytes = IPAddress.Parse(ip).GetAddressBytes();
ulong ret = 0;
foreach (var b in bytes)
{
ret <<= 8;
ret |= b;
} return ret;
} /// <summary>
/// 判断用户IP地址转换为Long型后是否在内网IP地址所在范围
/// </summary>
/// <param name="userIp">用户IP</param>
/// <param name="begin">开始范围</param>
/// <param name="end">结束范围</param>
/// <returns></returns>
private static bool IsInner(ulong userIp, ulong begin, ulong end)
{
return (userIp >= begin) && (userIp <= end);
} /// <summary>
/// 获取局域网IP
/// </summary>
/// <returns></returns>
private static string GetLanIp()
{
foreach (var hostAddress in Dns.GetHostAddresses(Dns.GetHostName()))
{
if (hostAddress.AddressFamily == AddressFamily.InterNetwork)
{
return hostAddress.ToString();
}
}
return string.Empty;
}
#endregion

Nginx 反向代理获取真实IP问题的更多相关文章

  1. php nginx反向代理获取真实ip的教程

    php nginx反向代理获取真实ip的教程 <pre> location /getip { proxy_pass http://newmiracle.cn/ip.php; } proxy ...

  2. 后端Apache获取前端Nginx反向代理的真实IP地址 (原创贴-转载请注明出处)

    ====================说在前面的话==================== 环境:前段Nginx是反向代理服务器:后端是Apache是WEB项目服务器 目的:让后端Apapche获取 ...

  3. nginx 反向代理 取得真实IP和域名

    nginx反向代理后,在应用中取得的ip都是反向代理服务器的ip,取得的域名也是反向代理配置的url的域名,解决该问题,需要在nginx反向代理配置中添加一些配置信息,目的将客户端的真实ip和域名传递 ...

  4. JAVA中经过nginx反向代理获取客户端ip并获取相关坐标等信息

    关于搜狐新浪ip库查询接口的使用 直接输出访客ip及所在城市: <script src="http://pv.sohu.com/cityjson?ie=utf-8" > ...

  5. nginx反向代理获取用户真实ip

    nginx做反向代理时,默认的配置后端获取到的ip都是来自于nginx,如何转发用户的真实ip到后端程序呢?如是是java后端,用request.getRemoteAddr();获取到的是nginx的 ...

  6. 阿里云SLB后Nginx、Tomcat获取真实IP

    一.SLB后Nginx如何获取真实IP 前提:nginx作为slb获取真实ip是使用 http_realip_module,默认一键安装包安装的nginx没有安装这个模块需要重新重新编译nginx并加 ...

  7. Nginx 反向代理获取设备真实的IP地址

    package com.das.common.util; import org.apache.commons.lang3.StringUtils; import org.springframework ...

  8. 关于.net core使用nginx做反向代理获取客户端ip的问题

    1.正常情况下.net core获取客户端ip是比较简单的 /// <summary> /// 获取客户Ip /// </summary> /// <param name ...

  9. nginx 4层tcp代理获取真实ip

    举个例子,Nginx 中的代理配置假如是这样配置的: location / { proxy_http_version 1.1; proxy_set_header X-Real-IP $remote_a ...

随机推荐

  1. 防止UI穿透操作到游戏场景

    #if UNITY_EDITOR || UNITY_STANDALONE_WIN if (EventSystem.current.IsPointerOverGameObject()) { return ...

  2. Apache OFBiz源码解读之MVC模型

    节点解析 request-map 你可以将其理解为controller的配置,如果你了解或使用过struts的配置或springmvc的annotation,就会发现这个定义跟它们是很相似的: [ht ...

  3. Ajax中的JSON格式与php传输过程的浅析

    在Ajax中的JSON格式与php传输过程中有哪些要注意的小地方呢? 先来看一下简单通用的JSON与php传输数据的代码 HTML文件: <input type="button&quo ...

  4. java.sql.SQLException: Can not issue data manipulation statements with executeQuery().

    1.错误描写叙述 java.sql.SQLException: Can not issue data manipulation statements with executeQuery(). at c ...

  5. Maven构建真正的J2EE项目

    今天同事问起我眼下用Maven构建的多模块项目架构和曾经用Eclipse创建的Web项目的问题.以下将讲一下使用maven搭建多模块的J2ee项目,以及採用这样的方式搭建项目对日后项目的水平拆分和垂直 ...

  6. JAVA入门[9]-mybatis多表关联查询

    概要 本节要实现的是多表关联查询的简单demo.场景是根据id查询某商品分类信息,并展示该分类下的商品列表. 一.Mysql测试数据 新建表Category(商品分类)和Product(商品),并插入 ...

  7. 如何通过PowerShell获取Office 365 TenantID

    作者:陈希章 发表于2017年5月31日 安装Azure Powershell 模块 Installing the Azure PowerShell Service Management module ...

  8. 十、 Spring Boot Shiro 权限管理

    使用Shiro之前用在spring MVC中,是通过XML文件进行配置. 将Shiro应用到Spring Boot中,本地已经完成了SpringBoot使用Shiro的实例,将配置方法共享一下. 先简 ...

  9. Python笔记·第九章—— 函数 (一)

    一.函数的作用 函数可以让我们代码结构更清晰,而且避免了代码的重复,冗余,使一段代码或者功能可以反复的被调用,大大提高了开发效率 二.函数的定义 def 函数名(参数1,参数2,*args,默认参数, ...

  10. CNN中的卷积操作的参数数计算

    之前一直以为卷积是二维的操作,而到今天才发现卷积其实是在volume上的卷积.比如输入的数据是channels*height*width(3*10*10),我们定义一个核函数大小为3*3,则输出是8* ...