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地址為:\n{0}",ipaddr));
              }
          }
       }
       }
       }

 

綜合上述的得到IP地址,可以發現,其實並不能完全的到真實的IP地址。因為IP地址是可以偽造的。所以大家可以通過這種方式取得。但是一定要做一些特殊的判斷及其處理,防止插入到數據庫中,引起異常現象。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM