這是一個非常簡單的問題,但是在信息傳播過程中,發生了一些錯誤。
比如一些人會說,你配置 X-Real-Ip 啊,這樣就行了。
或者有些人會問,X-Real-Ip 和 X-Forward-For 有什麼區別,原理是什麼?
儼然是把 X-Real-Ip 給誤解了。
本文帶你看看,到底什麼是 X-Real-Ip。
快速回答
X-Real-Ip 什麼也不是。你可以使用 My-Real-Ip,His-Real-Ip,隨便什麼字符串。
1server {
2 ...
3 location / {
4 ...
5 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
6 proxy_set_header Host $host;
7 proxy_set_header His-Real-Ip2 $remote_addr;
8 }
9}
以上配置設置了三個請求頭,分別是 X-Forwarded-For、Host、His-Real-Ip2,後臺 java 服務端可以根據 His-Real-Ip2 獲取真實的 IP。
聽起來很傷人,可是 X-Real-Ip 真的只是看起來是官方的、某個隱藏的字段,實際上它並不是。
不要再問什麼 X-Real-Ip 的原理是什麼?
或者 特殊的請求頭 X-Real-Ip
這樣的問題了,實際上真正的值,是 nginx 的內置變量 $remote_addr。
Docker 中的 nginx
docker 中的 nginx 可以獲取到的 $remote_addr,可能是 172.*.0.1。這些地址,是容器網橋的地址。
那麼如何獲取到用戶的真實 ip 呢?你的 nginx 容器必須使用主機網絡。
以下是一個 docker compose 示例:
1 my-nginx:
2 restart: always
3 container_name: my-nginx
4 image: nginx:latest
5 network_mode: host # note this mode is host
6 my-server:
7 restart: always
8 container_name: my-server
9 networks:
10 - spring_cloud_default
11 networks:
12 spring_cloud_default:
13 driver: bridge
在你的後端代碼中,考慮生產和開發環境,你應該做如下判斷(以 java 爲例,假設我們使用 His-Real-Ip2):
1RequestAttributes ra = RequestContextHolder.getRequestAttributes();
2ServletRequestAttributes sra = (ServletRequestAttributes) ra;
3if (null != sra) {
4 HttpServletRequest request = sra.getRequest();
5 realIp = request.getHeader("His-Real-Ip"); // 假設你有兩個 nginx,做了兩層轉發,第一層 nginx 設置了 His-Real-Ip
6 if (notValidIp(realIp)) { // notValidIp 可以自己實現,比如不以 172 開頭,或者 192.168 等等開頭
7 realIp = request.getHeader("His-Real-Ip2"); // 第二層 nginx 設置了 His-Real-Ip2。通過這樣的設置,就能區分哪些流量是走網關進來,哪些是直連進來。沒什麼用,只是作爲一個說明
8 if (notValidIp(realIp)) {
9 realIp = request.getRemoteAddr();
10 }
11 }
12}
結束語
好了,在技術工作過程中,我們不要以訛傳訛,雖然這是一個簡單的問題,但是要說清它,並不簡單。