發生的場景:服務器端接收客戶端請求的時候,通常須要進行簽名驗證,客戶端IP限定等狀況,在進行客戶端IP限定的時候,須要首先獲取該真實的IP。通常分爲兩種狀況:html
方式1、客戶端未通過代理,直接訪問服務器端(nginx,squid,haproxy);nginx
方式2、客戶端經過多級代理,最終到達服務器端(nginx,squid,haproxy);服務器
客戶端請求信息都包含在HttpServletRequest中,能夠經過方法getRemoteAddr()得到該客戶端IP。此時若是在使用方式一形式,能夠直接得到該客戶端真實IP。而若是是方式二中經過代理的形式,此時通過多級反向的代理,經過方法getRemoteAddr()得不到客戶端真實IP,能夠經過x-forwarded-for得到轉發後請求信息。當客戶端請求被轉發,IP將會追加在其後並以逗號隔開,例如:10.47.103.13,4.2.2.2,10.96.112.230。frontend
請求中的參數:ui
request.getHeader("x-forwarded-for") : 10.47.103.13,4.2.2.2,10.96.112.230spa
request.getHeader("X-Real-IP") : 10.47.103.13代理
request.getRemoteAddr():10.96.112.230code
客戶端訪問通過轉發,IP將會追加在其後並以逗號隔開。最終準確的客戶端信息爲:server
代碼示例:htm
/** * 獲取用戶真實IP地址,不使用request.getRemoteAddr()的緣由是有可能用戶使用了代理軟件方式避免真實IP地址, * 但是,若是經過了多級反向代理的話,X-Forwarded-For的值並不止一個,而是一串IP值 * * @return ip */ private String getIpAddr(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); System.out.println("x-forwarded-for ip: " + ip); if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) { // 屢次反向代理後會有多個ip值,第一個ip纔是真實ip if( ip.indexOf(",")!=-1 ){ ip = ip.split(",")[0]; } } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); System.out.println("Proxy-Client-IP ip: " + ip); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); System.out.println("WL-Proxy-Client-IP ip: " + ip); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); System.out.println("HTTP_CLIENT_IP ip: " + ip); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); System.out.println("HTTP_X_FORWARDED_FOR ip: " + ip); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("X-Real-IP"); System.out.println("X-Real-IP ip: " + ip); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); System.out.println("getRemoteAddr ip: " + ip); } System.out.println("獲取客戶端ip: " + ip); return ip; }
此時,正常狀況之下能夠獲取客戶端真實的IP。須要注意的是對於服務器端採用負載的形式,須要配置保存x-forwarded-for。目前負載的形式有haproxy、nginx等形式。結構圖以下:
我接觸的服務器端配置負載使用haproxy,相對於的配置以下:
frontend crmtest bind :8081 mode http option httplog option forwardfor // 配置HAProxy會把客戶端的IP信息發送給服務器,在HTTP請求中添加"X-Forwarded-For"字段。 default_backend drm backend drm mode http option httplog balance roundrobin stick-table type ip size 200k expire 30m stick on src server jboss1 10.10.10.11:8081 server jboss2 10.10.10.12:8081
haproxy配置參考:http://www.cnblogs.com/dkblog/archive/2012/03/13/2393321.html