無Nginx代理場景
業務層通過獲取請求頭引數即可拿到客戶端IP
request.getRemoteAddr();
複製程式碼
一級Nginx代理
使用代理後直接讀取請求頭引數會讀取到代理伺服器的IP地址,而非真實客戶端IP
解決方法是新增Nginx請求頭引數提前儲存客戶端IP
nginx.conf
配置加入內容
location / {
...
# IP地址轉發
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Real-Port $remote_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
複製程式碼
業務層讀取nginx配置的請求頭引數即可
public static String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("X-Real-IP");
if (ip == null || ip.length() == 0 || " unknown ".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
log.info("【Proxy-Client-IP】 {}", ip);
}
if (ip == null || ip.length() == 0 || " unknown ".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
log.info("【WL-Proxy-Client-IP】{}", ip);
}
if (ip == null || ip.length() == 0 || " unknown ".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Forwarded-For");
log.info("【X-Forwarded-For】{}", ip);
}
if (ip == null || ip.length() == 0 || " unknown ".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
log.info("【unknown】{}", ip);
}
return ip;
}
複製程式碼
多級Nginx代理
若存在多級Nginx代理,則需要在第一級代理時獲取客戶端IP,在後續代理逐層傳遞
第一級代理配置同上,第N級代理配置nginx.conf
如下
location /{
# IP地址轉發
proxy_set_header X-Real-IP $X-Real-IP;
proxy_set_header X-Real-Port $X-Real-Port;
proxy_set_header X-Forwarded-For $X-Forwarded-For;
}
複製程式碼
業務層保持不變即可讀取到傳遞的IP地址
阿里雲CDN轉發
在已經存在nginx代理的場景下,加入CDN後源客戶端IP在CDN處被轉發,故第一級nginx代理使用$remote_addr
引數讀取到的是CDN服務的地址,而根據一般約定,CDN轉發會將IP地址存放在X-Forwarded-For
引數下
修改頂級Nginx配置以支援優先獲取CDN代理地址,也可以通過修改業務層讀取優先順序實現
location / {
...
# IP地址轉發
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Real-Port $remote_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
複製程式碼
但經過對阿里雲CDN測試,發現X-Forwarded-For
下不僅包含真實客戶端IP也包含CDN服務IP,故業務層需要做一定的處理進行區分
如圖,第一個為真實客戶端IP,第二個為CDN代理IP
public static String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("X-Real-IP");
if (ip == null || ip.length() == 0 || " unknown ".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
log.info("【Proxy-Client-IP】 {}", ip);
}
if (ip == null || ip.length() == 0 || " unknown ".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
log.info("【WL-Proxy-Client-IP】{}", ip);
}
if (ip == null || ip.length() == 0 || " unknown ".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Forwarded-For");
if (ip.contains(",")) {
// 通過阿里雲CDN轉發後可能讀取到2個IP地址
String[] cdnMutilIp = ip.split(",");
ip = cdnMutilIp[0];
}
log.info("【X-Forwarded-For】{}", ip);
}
if (ip == null || ip.length() == 0 || " unknown ".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
log.info("【unknown】{}", ip);
}
return ip;
}
}
複製程式碼