通常的應用都是經過Nginx來作爲反向代理,而且Nginx還多是多層的。若是想在內部服務裏面獲取最原始的客戶端IP地址。則須要作一些配置html
爲了防止X-Forwarded-For頭的僞造,可在最外層Nginx將X-Forwarded-For設置爲真實IP$remote_addr。 $remote_addr是獲取的是直接TCP鏈接的客戶端IP(相似於Java中的request.getRemoteAddr()),這個是沒法僞造的,即便客戶端僞造也會被覆蓋掉,而不是追加。java
upstream innerservice { server 192.168.12.33:9001; server 192.168.12.34:9002; } server { listen 9000; server_name 192.168.12.22; root /usr/share/nginx/html; include /etc/nginx/default.d/*.conf; location / { proxy_pass http://innerservice; proxy_set_header Host $host:$server_port; proxy_set_header X-Real-PORT $remote_port; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; } }
upstream innerservice { server 192.168.12.33:9001; server 192.168.12.34:9002; } server { listen 9000; // xxx location / { proxy_pass http://innerservice; proxy_set_header Host $host:$server_port; proxy_set_header X-Real-PORT $remote_port; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
/** * Description:客戶端信息獲取過濾器 */ public class ClientContextFilter extends OncePerRequestFilter { /** * Nginx默認增長的IP地址頭 */ public static final String HEADER_X_FORWARDED_FOR = "X-Forwarded-For"; /** * 內部微服務之間調用增長的IP地址頭 */ public static final String HEADER_X_REMOTE_USER_IP = "X-Remote-User-IP"; /** * IP響應頭的分割符號 */ private static final String IP_HEADER_DELIMITER = ","; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { String remoteUserIP = loadRemoteUserIP(request); ClientContext context = new ClientContext(); context.setClientIP(request.getRemoteAddr()); context.setRemoteUserIP(remoteUserIP); ClientContextHolder.setContext(context); filterChain.doFilter(request, response); } finally { ClientContextHolder.clearContext(); } } private String loadRemoteUserIP(HttpServletRequest request) { String xForwardedHeader = request.getHeader(HEADER_X_FORWARDED_FOR); // 先嚐試從X-Forwarded-For獲取 if (xForwardedHeader != null && xForwardedHeader.trim().length() > 0) { String[] ips = xForwardedHeader.split(IP_HEADER_DELIMITER); return ips[0].trim(); } else { // 嘗試從內部自定義頭上獲取 String internalIPHeader = request.getHeader(HEADER_X_REMOTE_USER_IP); if (null != internalIPHeader && internalIPHeader.trim().length() > 0) { return internalIPHeader.trim(); } else { return null; } } } }
原理是在服務日後面傳遞的時候,定義一個自定義頭,將真實IP放到這個header中。 後臺服務可取兩個IP地址nginx
request.getRemoteAddr()
- 獲取調用方服務的內網IP地址loadRemoteUserIP
- 獲取最源頭的真實客戶端IP地址