1. 程式人生 > 實用技巧 >Nginx多層代理獲取真實客戶端IP【轉載】

Nginx多層代理獲取真實客戶端IP【轉載】

一般的應用都是通過Nginx來做為反向代理,並且Nginx還可能是多層的。如果想在內部服務裡面獲取最原始的客戶端IP地址。則需要做一些配置

最外層Nginx

為了防止X-Forwarded-For頭的偽造,可在最外層Nginx將X-Forwarded-For設定為真實IP$remote_addr。 $remote_addr是獲取的是直接TCP連線的客戶端IP(類似於Java中的request.getRemoteAddr()),這個是無法偽造的,即使客戶端偽造也會被覆蓋掉,而不是追加。

    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地址

  • request.getRemoteAddr()- 獲取呼叫方服務的內網IP地址
  • loadRemoteUserIP- 獲取最源頭的真實客戶端IP地址

原文出處:https://my.oschina.net/yidao620c/blog/3098849