測試代碼
@RequestMapping("/hello1")
public Map getIp1(HttpServletRequest request) {
Enumeration<String> names = request.getHeaderNames();
ArrayList<String> array = new ArrayList<String>();
while (names.hasMoreElements()){
array.add(names.nextElement());
}
Map<String, String> map = new HashMap<>();
for (int i = 0; i < array.size(); i++) {
String key = array.get(i);
map.put(key, request.getHeader(key));
}
map.put("侯萬getRemoteAddr", request.getRemoteAddr());
String ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
// 根據(jù)網(wǎng)卡取本機(jī)配置的IP
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress = inet.getHostAddress();
}
}
// 對于通過多個(gè)代理的情況编曼,第一個(gè)IP為客戶端真實(shí)IP,多個(gè)IP按照','分割
if (ipAddress != null && ipAddress.length() > 15) { //"***.***.***.***".length() = 15
if (ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
}
map.put("ipAddress", ipAddress);
return map;
}
資料1:
如果服務(wù)器使用了Nginx
代理,需要設(shè)置Nginx
的配置畜侦,才能獲取到IP元扔,因?yàn)榻?jīng)過反向代理后,由于在客戶端和web服務(wù)器之間增加了中間層旋膳,因此web服務(wù)器無法直接拿到客戶端的ip澎语,只能通過$remote_addr
變量拿到的將是反向代理服務(wù)器的ip地址。
我在default.conf
的配置是:
server {
listen 443 ssl; # 支持ipv4
listen [:]:443 ssl; # 支持ipv6
location /hello {
proxy_pass http://182.92.106.69:8848/hello1/;
proxy_redirect off;
# 關(guān)鍵的四句代碼
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Nginx
中的幾個(gè)變量解釋:
-
$remote_addr
代表客戶端的IP验懊,但它的值不是由客戶端提供的擅羞,而是服務(wù)端根據(jù)客戶端的ip指定的,icanhazip的原理也是這樣义图, 當(dāng)你的瀏覽器訪問某個(gè)網(wǎng)站時(shí)减俏,假設(shè)中間沒有任何代理,那么網(wǎng)站的web服務(wù)器就會把remote_addr
設(shè)為你在公網(wǎng)暴露的IP碱工,如果你用了某個(gè)代理娃承,那么你的瀏覽器會先訪問這個(gè)代理,然后再由這個(gè)代理轉(zhuǎn)發(fā)到網(wǎng)站怕篷,這樣web服務(wù)器就會把remote_addr
設(shè)為這臺代理機(jī)器的IP, 除非代理將你的IP附在請求header中一起轉(zhuǎn)交給web服務(wù)器历筝。 -
$proxy_add_x_forwarded_for
全局變量$proxy_add_x_forwarded_for
包含客戶端請求頭中的X-Forwarded-For
,與$remote_addr
兩部分匙头,他們之間用逗號分開漫谷。X-Forwarded-For
(簡稱XFF),X-Forwarded-For
是一個(gè) HTTP 擴(kuò)展頭部蹂析。RFC 2616 協(xié)議并沒有對它的定義舔示,它最開始是由Squid
這個(gè)緩存代理軟件引入,用來表示 HTTP 請求端真實(shí) IP电抚。如今它已經(jīng)成為事實(shí)上的標(biāo)準(zhǔn)惕稻,被各大HTTP 代理、負(fù)載均衡等轉(zhuǎn)發(fā)服務(wù)廣泛使用蝙叛,并被寫入 RFC 7239(Forwarded HTTP Extension` 標(biāo)準(zhǔn)之中俺祠。 -
$proxy_set_header
,可設(shè)置代理后 header -
X-Real-IP
一般比如X-Real-IP
這一個(gè)自定義頭部字段,通常被 HTTP 代理用來表示與它產(chǎn)生TCP 連接的設(shè)備 IP蜘渣,這個(gè)設(shè)備可能是其他代理淌铐,也可能是真正的請求端,這個(gè)要看經(jīng)過代理的層級次數(shù)或是是否始終將真實(shí)IP一路傳下來蔫缸。(牢記:任何客戶端傳上來的東西都是不可信的)
當(dāng)多層代理或使用CDN時(shí)腿准,如果代理服務(wù)器不把用戶的真實(shí)IP傳遞下去,那么服務(wù)器將永遠(yuǎn)不可能獲取到用戶的真實(shí)IP拾碌。
資料2:
上面存在問題是吐葱,可能一個(gè)基站,或者一個(gè)小區(qū)校翔,對外只有一個(gè)公共IP弟跑。因此這種情況下,同一ip出現(xiàn)數(shù)百用戶也是正常的防症。
典型的是:一個(gè)公司內(nèi)孟辑,大家共同用同一個(gè)網(wǎng)絡(luò),對外都是一個(gè)的公網(wǎng)IP蔫敲。
資料3:
私有的內(nèi)部組網(wǎng)IP段(可作為代碼直接判斷):
- A類:10.0.0.0 — 10.255.255.255 10.0.0.0/8
- B類:172.16.0.0 — 172.31.255.255 172.16.0.0/12
- C類:192.168.0.0 — 192.168.255.255 192.168.0.0/16
資料4:
參考文檔: