在開發(fā)工作中,我們常常需要獲取客戶端的IP,。一般獲取客戶端的IP地址的方法是:request.getRemoteAddr();但是在通過了Apache,Squid等反向代理軟件就不能獲取到客戶端的真實IP地址了。 原因:由于在客戶端和服務之間增加了中間代理,因此服務器無法直接拿到客戶端的IP,服務器端應用也無法直接通過轉發(fā)請求的地址返回給客戶端,。 現(xiàn)在圖示代理上網和IP的關系:
第一種情況:不通過代理上網,服務器端拿到真實IP
第二種情況:通過代理服務器如:Nginx,Squid等一層代理或多層代理上網,,如下圖:
需要注意的是X-Forwarded-For和X-Real-IP都不是http的正式協(xié)議頭,,而是squid等反向代理軟件最早引入的,,之所以resin能拿到,,是因為NGINX里一般缺省都會這么配置轉發(fā)的http請求: location / { proxy_pass http://; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; },從X-Forwarded-For的定義來看,,ips[0]才是原始客戶端ip,,如果這個都不是,那拿第二個就更不靠譜了,,我們平時檢驗的時候,,可能是直接在內網掛代理去訪問的,跟外面網友訪問經過的網絡路徑不一樣,,后面不停添加的是經過的每一層代理ip才對,下面舉例說明; request.getRemoteAddr() 192.168.239.196 request.getHeader("X-Forwarded-For") 58.63.227.162, 192.168.237.178, 192.168.238.218 request.getHeader("X-Real-IP") 192.168.238.218 所以訪問的流程應該是這樣,,客戶端58.63.227.162發(fā)出請求,經過192.168.237.178, 192.168.238.218兩層轉發(fā),,到了192.168.239.196這臺NGINX上,,NGINX就把X-Real-IP頭設成了自己看到的remote_addr,也就是直接發(fā)給到他的192.168.238.218,,這時候resin收到這個包,,對resin來說直接發(fā)給他的remote_addr就是NGINX的ip,也就是192.168.239.196,,那么resin里面的request.getRemoteAddr()就是192.168.239.196,,那么在resin里拿最原始的ip邏輯(也就是拿能夠知道的最外層的ip)應該是這樣: 如果XFF不為空,拿XFF的左邊第一個 如果XFF為空,,拿XRI 如果XRI為空,,只能拿request.getRemoteAddr(),也就是只能拿到最直接發(fā)給他的機器ip了,, 其他都不可考究,參考代碼如下: 第一種代碼: /**
* 從Request對象中獲得客戶端IP,,處理了HTTP代理服務器和Nginx的反向代理截取了ip
* @param request
* @return ip
*/
public static String getLocalIp(HttpServletRequest request) {
String remoteAddr = request.getRemoteAddr();
String forwarded = request.getHeader("X-Forwarded-For");
String realIp = request.getHeader("X-Real-IP");
String ip = null;
if (realIp == null) {
if (forwarded == null) {
ip = remoteAddr;
} else {
ip = remoteAddr + "/" + forwarded.split(",")[0];
}
} else {
if (realIp.equals(forwarded)) {
ip = realIp;
} else {
if(forwarded != null){
forwarded = forwarded.split(",")[0];
}
ip = realIp + "/" + forwarded;
}
}
return ip;
}
第二種代碼: 1 public static String getIp(HttpServletRequest request) {
2 String remoteAddr = request.getRemoteAddr();
3 String forwarded = request.getHeader("X-Forwarded-For");
4 String realIp = request.getHeader("X-Real-IP");
5
6 String ip = null;
7 if (realIp == null) {
8 if (forwarded == null) {
9 ip = remoteAddr;
10 } else {
11 ip = remoteAddr + "/" + forwarded;
12 }
13 } else {
14 if (realIp.equals(forwarded)) {
15 ip = realIp;
16 } else {
17 ip = realIp + "/" + forwarded.replaceAll(", " + realIp, "");
18 }
19 }
20 return ip;
21 }
第三種代碼: 1 public static String getIp2(HttpServletRequest request) {
2 String ip = request.getHeader("X-Forwarded-For");
3 if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){
4 //多次反向代理后會有多個ip值,第一個ip才是真實ip
5 int index = ip.indexOf(",");
6 if(index != -1){
7 return ip.substring(0,index);
8 }else{
9 return ip;
10 }
11 }
12 ip = request.getHeader("X-Real-IP");
13 if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){
14 return ip;
15 }
16 return request.getRemoteAddr();
17 }
第三種是最合適的,,最清晰理解的
|
|