獲取用戶真實IP

Docker Nginx 獲取用戶真實 IP

這是一個非常簡單的問題,但是在信息傳播過程中,發生了一些錯誤。

比如一些人會說,你配置 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}

結束語

好了,在技術工作過程中,我們不要以訛傳訛,雖然這是一個簡單的問題,但是要說清它,並不簡單。