Nginx多層代理獲取真實客戶端IP

通常的應用都是經過Nginx來作爲反向代理,而且Nginx還多是多層的。若是想在內部服務裏面獲取最原始的客戶端IP地址。則須要作一些配置html

最外層Nginx

爲了防止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;
        }
    }

後續Nginx配置

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;
        }
    }

Java中獲取真實IP

/**
 * 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地址
相關文章
相關標籤/搜索