|
|
51CTO旗下网站
|
|
移动端

Nginx反向代理获取真实IP问题

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

作者:jianxuanbing来源:博客园|2018-01-10 10:15

 一、前言

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

回到顶部

二、原因

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

回到顶部

三、解决方案

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

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

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

3.1 Nginx 配置如下

  1. server { 
  2.         listen       9461; # 监听端口号 
  3.         server_name  localhost 192.168.88.22; # 访问地址 
  4.         location / { 
  5.             root   项目路径; # 例如:E:/Publish/xxx/; 
  6.             index  index.html; 
  7.              
  8.             # 此处用于处理 Vue、Angular、React 使用H5 的 History时 重写的问题 
  9.             if (!-e $request_filename) { 
  10.                 rewrite ^(.*) /index.html last
  11.                 break; 
  12.             } 
  13.         } 
  14.          
  15.         # 代理服务端接口 
  16.         location /api { 
  17.             proxy_pass http://localhost:9460/api;# 代理接口地址 
  18.             # Host配置以及域名传递 
  19.             proxy_set_header Host $host; 
  20.             proxy_set_header X-Real-IP $remote_addr; 
  21.             proxy_set_header REMOTE-HOST $remote_addr; 
  22.             proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
  23.         } 
  24.     } 

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

  1. #region Ip(客户端IP地址) 
  2.  
  3. /// <summary> 
  4. /// 客户端IP地址 
  5. /// </summary> 
  6. public static string Ip 
  7.     get 
  8.     { 
  9.         var result = string.Empty; 
  10.         if (HttpContext.Current != null
  11.         { 
  12.             result = GetWebClientIp(); 
  13.         } 
  14.         if (string.IsNullOrWhiteSpace(result)) 
  15.         { 
  16.             result = GetLanIp(); 
  17.         } 
  18.         return result; 
  19.     } 
  20.  
  21. /// <summary> 
  22. /// 获取Web客户端的IP 
  23. /// </summary> 
  24. /// <returns></returns
  25. private static string GetWebClientIp() 
  26.     var ip = GetWebProxyRealIp() ?? GetWebRemoteIp(); 
  27.     foreach (var hostAddress in Dns.GetHostAddresses(ip)) 
  28.     { 
  29.         if (hostAddress.AddressFamily == AddressFamily.InterNetwork) 
  30.         { 
  31.             return hostAddress.ToString(); 
  32.         } 
  33.     } 
  34.     return string.Empty; 
  35.  
  36. /// <summary> 
  37. /// 获取Web远程IP 
  38. /// </summary> 
  39. /// <returns></returns
  40. private static string GetWebRemoteIp() 
  41.     try 
  42.     { 
  43.         return HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"] ?? 
  44.                HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"] ?? ""
  45.     } 
  46.     catch (Exception e) 
  47.     { 
  48.         return string.Empty; 
  49.     } 
  50.  
  51. /// <summary> 
  52. /// 获取Web代理真实IP 
  53. /// </summary> 
  54. /// <returns></returns
  55. private static string GetWebProxyRealIp() 
  56.     var request = HttpContext.Current.Request; 
  57.     string ip = request.Headers.Get("x-forwarded-for"); 
  58.  
  59.     if (string.IsNullOrEmpty(ip) || string.Equals("unknown", ip, StringComparison.OrdinalIgnoreCase)) 
  60.     { 
  61.         ip = request.Headers.Get("Proxy-Client-IP"); 
  62.     } 
  63.  
  64.     if (string.IsNullOrEmpty(ip) || string.Equals("unknown", ip, StringComparison.OrdinalIgnoreCase)) 
  65.     { 
  66.         ip = request.Headers.Get("WL-Proxy-Client-IP"); 
  67.     } 
  68.  
  69.     if (string.IsNullOrEmpty(ip) || string.Equals("unknown", ip, StringComparison.OrdinalIgnoreCase)) 
  70.     { 
  71.         ip = request.UserHostAddress; 
  72.     } 
  73.  
  74.     if (string.IsNullOrEmpty(ip)) 
  75.     { 
  76.         return string.Empty; 
  77.     } 
  78.     // 可能存在如下格式:X-Forwarded-For: client, proxy1, proxy2 
  79.     if (ip.Contains(", ")) 
  80.     { 
  81.         // 如果存在多个反向代理,获得的IP是一个用逗号分隔的IP集合,取第一个 
  82.         // X-Forwarded-For: client  第一个 
  83.         string[] ips = ip.Split(new string[1] {", "}, StringSplitOptions.RemoveEmptyEntries); 
  84.         var i = 0; 
  85.         for (i = 0; i < ips.Length; i++) 
  86.         { 
  87.             if (ips[i] != ""
  88.             { 
  89.                 // 判断是否为内网IP 
  90.                 if (false == IsInnerIp(ips[i])) 
  91.                 { 
  92.                     IPAddress realIp; 
  93.                     if (IPAddress.TryParse(ips[i], out realIp) && ips[i].Split('.').Length == 4) 
  94.                     { 
  95.                         //合法IP 
  96.                         return ips[i]; 
  97.                     } 
  98.  
  99.                     return ""
  100.                 } 
  101.             } 
  102.         } 
  103.  
  104.         ip = ips[0];// 默认获取第一个IP地址 
  105.     } 
  106.  
  107.     return ip; 
  108.  
  109.  
  110. /// <summary> 
  111. /// 判断IP地址是否为内网IP地址 
  112. /// </summary> 
  113. /// <param name="ip">IP地址</param> 
  114. /// <returns></returns
  115. private static bool IsInnerIp(string ip) 
  116.     bool isInnerIp = false
  117.     ulong ipNum = Ip2Ulong(ip); 
  118.  
  119.     /** 
  120.      * 私有IP 
  121.      * A类:10.0.0.0-10.255.255.255 
  122.      * B类:172.16.0.0-172.31.255.255 
  123.      * C类:192.168.0.0-192.168.255.255 
  124.      * 当然,还有127这个网段是环回地址 
  125.      */ 
  126.  
  127.     ulong aBegin = Ip2Ulong("10.0.0.0"); 
  128.     ulong aEnd = Ip2Ulong("10.255.255.255"); 
  129.     ulong bBegin = Ip2Ulong("172.16.0.0"); 
  130.     ulong bEnd = Ip2Ulong("10.31.255.255"); 
  131.     ulong cBegin = Ip2Ulong("192.168.0.0"); 
  132.     ulong cEnd = Ip2Ulong("192.168.255.255"); 
  133.  
  134.     isInnerIp = IsInner(ipNum, aBegin, aEnd) || IsInner(ipNum, bBegin, bEnd) || IsInner(ipNum, cBegin, cEnd) || 
  135.                 ip.Equals("127.0.0.1"); 
  136.     return isInnerIp; 
  137.  
  138. /// <summary> 
  139. /// 将IP地址转换为Long型数字 
  140. /// </summary> 
  141. /// <param name="ip">IP地址</param> 
  142. /// <returns></returns
  143. private static ulong Ip2Ulong(string ip) 
  144.     byte[] bytes = IPAddress.Parse(ip).GetAddressBytes(); 
  145.     ulong ret = 0; 
  146.     foreach (var b in bytes) 
  147.     { 
  148.         ret <<= 8; 
  149.         ret |= b; 
  150.     } 
  151.  
  152.     return ret; 
  153.  
  154. /// <summary> 
  155. /// 判断用户IP地址转换为Long型后是否在内网IP地址所在范围 
  156. /// </summary> 
  157. /// <param name="userIp">用户IP</param> 
  158. /// <param name="begin">开始范围</param> 
  159. /// <param name="end">结束范围</param> 
  160. /// <returns></returns
  161. private static bool IsInner(ulong userIp, ulong begin, ulong end
  162.     return (userIp >= begin) && (userIp <= end); 
  163.  
  164. /// <summary> 
  165. /// 获取局域网IP 
  166. /// </summary> 
  167. /// <returns></returns
  168. private static string GetLanIp() 
  169.     foreach (var hostAddress in Dns.GetHostAddresses(Dns.GetHostName())) 
  170.     { 
  171.         if (hostAddress.AddressFamily == AddressFamily.InterNetwork) 
  172.         { 
  173.             return hostAddress.ToString(); 
  174.         } 
  175.     } 
  176.     return string.Empty; 
  177. #endregion 

【编辑推荐】

  1. 从nginx日志原始二进制数据还原文件
  2. Web服务器如何选择 Apache or Nginx?
  3. 中国电信联手联想,Cavium和Panath发布基于智能网卡的vBRAS数据面性能测试报告
  4. Nginx、FastCGI、PHP-fpm关系梳理
  5. Nginx限制单个IP的并发连接数/速度来减缓垃圾蜘蛛爬虫采集
【责任编辑:武晓燕 TEL:(010)68476606】


点赞 0
分享:
大家都在看
猜你喜欢
24H热文
一周话题
本月最赞

视频课程+更多

热门职位+更多

读 书 +更多

Microsoft SQL Server 2005 技术内幕:T-SQL查询

本书是Inside Microsoft SQL Server 2005系列四本著作中的一本。它详细介绍了T-SQL的内部构造,包含了非常全面的编程参考。它提供了使用Tra...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊