nginx反向代理後,在應用中取得的ip都是反向代理服務器的ip,取得的域名也是反向代理配置的url的域名,解決該問題,須要在nginx反向代理配置中添加一些配置信息,目的將客戶端的真實ip和域名傳遞到應用程序中。nginx
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相關。url
先看下C#代碼的處理:spa
#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的判斷。code
#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。而後繼續作處理。orm
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地址是能夠僞造的。因此你們能夠經過這種方式取得。可是必定要作一些特殊的判斷及其處理,防止插入到數據庫中,引發異常現象。blog