阿里雲CDN + nginx多級代理獲取客戶端IP

無Nginx代理場景

業務層經過獲取請求頭參數便可拿到客戶端IPjava

request.getRemoteAddr();
複製代碼

一級Nginx代理

使用代理後直接讀取請求頭參數會讀取到代理服務器的IP地址,而非真實客戶端IPnginx

解決方法是添加Nginx請求頭參數提早保存客戶端IPbash

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以下spa

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參數下code

修改頂級Nginx配置以支持優先獲取CDN代理地址,也能夠經過修改業務層讀取優先級實現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

1554256476626.png

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;
    }
}
複製代碼
相關文章
相關標籤/搜索