• 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地址是可以伪造的。所以大家可以通过这种方式取得。但是一定要做一些特殊的判断及其处理,防止插入到数据库中,引起异常现象。

  • 相关阅读:
    Java后端工程师的学习技术栈
    ltp 分析 fail testcase
    程序员这个职业需要具备的素养
    你真的愿意到了50岁还要做编程吗?
    程序员的学习和积累
    程序员写博客的缘由
    VS2010生成文件
    从菜鸟到专家的五步编程语言学习法
    程序设计的18大原则
    怎样的代码才算是干净的代码?
  • 原文地址:https://www.cnblogs.com/nx520zj/p/6031337.html
Copyright © 2020-2023  润新知