nginx反向代理取得IP地址

摘要:
在nginx反向代理之后,应用程序中获得的IP是反向代理服务器的IP,获得的域名也是反向代理配置的URL的域名。要解决这个问题,您需要在nginx反向代理配置中添加一些配置信息,以便将客户端的真实IP和域名传输到应用程序。

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

nginx反向代理配置时,一般会添加下面的配置:

      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;

其中第一行关于host的配置,是关于域名传递的配置,余下跟IP相关。

先看下C#代码的处理:

#region 获取反向代理时的客户端的IP地址 getClientIP
 /// <summary>
/// 获取反向代理时的客户端的IP地址
 /// </summary>
 /// <returns>返回客户端真实IP</returns>
private string getClientIP()
{
    HttpRequestBase request = HttpContext.Request;

    string ip = request.Headers.Get("x-forwarded-for");
           
    if (ip == null || ip.Length == 0 || string.Equals("unknown", ip, StringComparison.OrdinalIgnoreCase))
    {
       ip = request.Headers.Get("Proxy-Client-IP");
    }
    if (ip == null || ip.Length == 0 || string.Equals("unknown", ip, StringComparison.OrdinalIgnoreCase))
    {
       ip = request.Headers.Get("WL-Proxy-Client-IP");

    }
     if (ip == null || ip.Length == 0 || string.Equals("unknown", ip, StringComparison.OrdinalIgnoreCase))
     {
           ip = request.UserHostAddress;
     }
       return ip;
   } 

但是需要注意的是,通过nginx反向代理后,如果访问IP通过了几层代理,可能取得的IP地址是这种格式:clientIP, proxy1, proxy2 .又可能需要进行插入数据库的话,防止数据库恶意注入。所以要针对上述IP地址的格式进行截取。

#region 获取反向代理时的客户端的IP地址 getClientIP
/// <summary>
/// 获取反向代理时的客户端的IP地址
/// </summary>
/// <returns>返回客户端真实IP</returns>
private string getClientIP()
{
    HttpRequestBase request = HttpContext.Request;

    string ip = request.Headers.Get("x-forwarded-for");
   
    if (ip == null || ip.Length == 0 || string.Equals("unknown", ip, StringComparison.OrdinalIgnoreCase))
    {
        ip = request.Headers.Get("Proxy-Client-IP");
    }
    if (ip == null || ip.Length == 0 || string.Equals("unknown", ip, StringComparison.OrdinalIgnoreCase))
    {
        ip = request.Headers.Get("WL-Proxy-Client-IP");

    }
    if (ip == null || ip.Length == 0 || string.Equals("unknown", ip, StringComparison.OrdinalIgnoreCase))
    {
        ip = request.UserHostAddress;
    }
    //可能存在如下格式:X-Forwarded-For: client, proxy1, proxy2
    int i = 0;
    if(ip.Contains(", "))
    {
        //如果存在多个反向代理,获得的IP是一个用逗号分隔的IP集合,取第一个
        //X-Forwarded-For: client  第一个
        string[] ipaddrs = ip.Split(new string[1] { ", " },StringSplitOptions.RemoveEmptyEntries);
        
        for(i=0;i<ipaddrs.Length;i++)
        {
            if(ipaddrs[i]!="")
            {
                if (false == IsInnerIP(ipaddrs[i]))//判断是否为内网IP
                {
                    IPAddress realip;
                    if (IPAddress.TryParse(ipaddrs[i], out realip) && ipaddrs[i].Split('.').Length == 4)
                    {//合法IP
                        return ipaddrs[i];
                    }
                    else
                    {//非法IP
                        //IP地址不符合规范
                    }
                }
            }
        }
        ip = ipaddrs[0];//默认取第一个ip地址
    }
    
    return ip;
} 
#endregion

之前发现,虽然说截取了上述IP地址的第一个clientip,但是发现有时候读出来的这个ip地址为内网IP。所以要加上内网IP的判断。

        #region 判断IP地址是否为局域网内网地址
        /// <summary>
        /// 判断IP地址是否为内网IP地址
        /// </summary>
        /// <param name="ipAddress">IP地址字符串</param>
        /// <returns></returns>
        private  bool IsInnerIP(String ipAddress)
        {
            bool isInnerIp = false;
            ulong ipNum = ip2ulong(ipAddress);
            /**
               私有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("172.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) || ipAddress.Equals("127.0.0.1");
            return isInnerIp;
        }
        /// <summary>
        /// 把IP地址转换为Long型数字
        /// </summary>
        /// <param name="ipAddress">IP地址字符串</param>
        /// <returns></returns>
        private ulong ip2ulong(string ipAddress)
        {
            byte[] bytes = IPAddress.Parse(ipAddress).GetAddressBytes();
            ulong ret = 0;

            foreach (byte b in bytes)
            {
                ret <<= 8;
                ret |= b;
            }
            return ret;
        }
        /// <summary>
        /// 判断用户IP地址转换为Long型后是否在内网IP地址所在范围
        /// </summary>
        /// <param name="userIp"></param>
        /// <param name="begin"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        private bool IsInner(ulong userIp, ulong begin, ulong end)
        {
            return (userIp >= begin) && (userIp <= end);
        }
        #endregion

后面又发现,nginx反向代理,得到的IP地址格式是unknown, 86.15.56.29。然后继续做处理。

 if (ip.Contains(", "))
 {
    //如果存在多个反向代理,获得的IP是一个用逗号分隔的IP集合,取第一个
     //X-Forwarded-For: client  第一个
     string[] ipaddrs = ip.Split(new string[1] { ", " }, StringSplitOptions.RemoveEmptyEntries);
    ip = ipaddrs[0];//先默认取第一个IP地址
    foreach(string ipaddr in ipaddrs)
    {
     if (ipaddr != "" && ipaddr.Split('.').Length == 4 && string.Equals("unknown",ipaddr,StringComparison.OrdinalIgnoreCase) == false)
     {//对应一些特殊的获取的特殊IP地址结构 unknown, 86.15.56.29
          if (false == IsInnerIP(ipaddr))
          {
              IPAddress realip;
              if (IPAddress.TryParse(ipaddr, out realip))
              {//合法IP
                   ip = ipaddr;
                       break;//只要找到一个非内网的IP地址,则跳出循环
              }
              else 
              {//非法IP
                   LogHelper.writeLog(LogHelper.IP_THREAD_LOG + "_" + mApp, string.Format("非法IP地址为:
{0}",ipaddr));
              }
          }
       }
       }
       }

综合上述的得到IP地址,可以发现,其实并不能完全的到真实的IP地址。因为IP地址是可以伪造的。所以大家可以通过这种方式取得。但是一定要做一些特殊的判断及其处理,防止插入到数据库中,引起异常现象。

免责声明:文章转载自《nginx反向代理取得IP地址》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇JS 获取当前日期时间(兼容IE FF)数据库操作(六)、Date函数下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

自定义标签

在实际的开发中,如为了简化JSP中出现大量的JSP脚本,那么我们需要使用标准标签库和EL表达式,但是和新标签库中提供的标签是有限的,不可能完全满足开发的需要。如:分页。因此需要学习如何自定义自己的标签库。 如果要实现自定义标签,那么需要如下几步: 编写标签处理类 需要继承或者实现相关的类或者接口 编写标签描述文件 该文件是一个XML文件,而且必须放在网站的...

【转】 如何利用C#代码来进行操作AD

要用代码访问 Active Directory域服务,需引用System.DirectoryServices命名空间,该命名空间包含两个组件类,DirectoryEntry和 DirectorySearcher。DirectoryEntry类可封装 ActiveDirectory域服务层次结构中的节点或对象,使用此类绑定到对象、读取属性和更新特性;使用Di...

Java 后端微信支付demo

Java 后端微信支付demo 一、导入微信SDK 二、在微信商户平台下载证书放在项目的resources目录下的cert文件夹下(cert文件夹需要自己建) 三、实现微信的WXPayConfig接口 1 packagecom.ieou.demo.common; 2 3 4 importcom.github.wxpay.sdk.WXPayCo...

C#文件处理相关内容

C#修改文件内容和过滤有效内容,文件格式一定。 OpenFileDialog file = new OpenFileDialog();//定义新的文件打开位置控件 file.InitialDirectory =Application.StartupPath; file.Filter...

Java内存管理的小技巧

1.尽量使用直接量     当需要使用字符串,还有Byte,Short,Long,Float,Double,Boolean,Character包装类的实例时,程序不应该采用New的方式来创建对象,而应该直接采用直接量来创建它们,程序需要“hello”字符串时,应该采用String str=“hello”;如果以上述方式创建字符串,JVM的字符串缓存池会缓存...

ServletContextListener使用详解(监听Tomcat启动、关闭)

  在 Servlet API 中有一个 ServletContextListener 接口,它能够监听 ServletContext 对象的生命周期,实际上就是监听 Web 应用的生命周期。   当Servlet 容器启动或终止Web 应用时,会触发ServletContextEvent 事件,该事件由ServletContextListener 来处...