1-Intro

ๆœ‰ไธ€ไบ›็ฎ€ๅ•็š„ๅทฅๅ…ท็ฑป๏ผŒไฝ†ๆ˜ฏๆ„Ÿ่ง‰ tomcat ็š„ๅ†…้ƒจๅฎž็Žฐๆ›ดๅŠ ็š„้ ่ฐฑ๏ผŒ่ฟ™้‡Œๅˆ†ๆžไธ€ไธ‹ๆบ็ .

1)-็ฎ€ๅ•ๅฎž็Žฐ

ๆฃ€ๆŸฅไธ€ไธ‹็ฎ€ๅ•็š„ HTTP-HEADER

public static String getIP(HttpServletRequest request) {  
    Assert.notNull(request, "HttpServletRequest is null");  
    String ip = request.getHeader("X-Requested-For");  
    if (StrUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {  
        ip = request.getHeader("X-Forwarded-For");  
    }  
  
    if (StrUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {  
        ip = request.getHeader("Proxy-Client-IP");  
    }  
  
    if (StrUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {  
        ip = request.getHeader("WL-Proxy-Client-IP");  
    }  
  
    if (StrUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {  
        ip = request.getHeader("HTTP_CLIENT_IP");  
    }  
  
    if (StrUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {  
        ip = request.getHeader("HTTP_X_FORWARDED_FOR");  
    }  
  
    if (StrUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {  
        ip = request.getRemoteAddr();  
    }  
  
    return StrUtil.isBlank(ip) ? null : ip.split(",")[0];  
}
 

2)-TOMCAT ๆบ็ ๅˆ†ๆž

ๆบ็ ไฝ็ฝฎ:

  • org.apache.catalina.filters.RemoteIpFilter#doFilter(jakarta.servlet.http.HttpServletRequest, jakarta.servlet.http.HttpServletResponse, jakarta.servlet.FilterChain)

3)-็†่งฃไธ€ๆฌกไปฃ็†็š„่กŒไธบ

ๅ‡่ฎพๅชๆœ‰ไธ€ๅฑ‚ไปฃ็† NGINX, ็„ถๅŽ็”จๆˆท้€š่ฟ‡ๆต่งˆๅ™จ่ฎฟ้—ฎ https://www.example.com . ็„ถๅŽไผšๆœ‰ๅฆ‚ไธ‹็š„้—ฎ้ข˜:

  1. Web ๆœๅŠกๅ™จๅฆ‚ไฝ•็Ÿฅ้“ ๅŽŸๅง‹็š„่ฏทๆฑ‚ๆ˜ฏ HTTPS ;
  2. Web ๆœๅŠกๅ™จๅฆ‚ไฝ•็Ÿฅ้“็”จๆˆทๅฎž้™…ไธŠ่ฎฟ้—ฎ็š„ๆ˜ฏ www.example.com

่€Œไปฃ็†ๆœๅŠกๅ™จๆœ‰ไธ€ไบ›ๅฅ‘็บฆ็š„.

  • X-Forwarded-Proto : ไผš่ฏดๆ˜Ž ย Web ๆœๅŠกๅ™จๅŽŸๅง‹่ฏทๆฑ‚ไฝฟ็”จ็š„ๅ่ฎฎ๏ผˆHTTP ๆˆ– HTTPS๏ผ‰;
  • X-Forwarded-Host : ไผšๅ‘Š่ฏ‰ Web ๆœๅŠกๅ™จๅŽŸๅง‹่ฎฟ้—ฎ็š„ Host ๆ˜ฏไป€ไนˆ ;

2-Design

2-1 ๅ†…้ƒจไปฃ็†็ฝ‘ๆฎต

private Pattern internalProxies =  
        Pattern.compile("10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" + "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" +  
                "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" + "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" +  
                "100\\.6[4-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" + "100\\.[7-9]{1}\\d{1}\\.\\d{1,3}\\.\\d{1,3}|" +  
                "100\\.1[0-1]{1}\\d{1}\\.\\d{1,3}\\.\\d{1,3}|" + "100\\.12[0-7]{1}\\.\\d{1,3}\\.\\d{1,3}|" +  
                "172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" + "172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" +  
                "172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}|" + "0:0:0:0:0:0:0:1|::1");

IANA ๆŠŠ่ฟ™ไบ›็ฝ‘ๆฎตไฟ็•™ไธบ็งๆœ‰็ฝ‘ๆฎต.

  • 10.0.0.0 ๅˆฐ 10.255.255.255
  • 192.168.0.0 ๅˆฐ 192.168.255.255
  • 169.254.0.0 ๅˆฐ 169.254.255.255
  • 127.0.0.0 ๅˆฐ 127.255.255.255
  • 100.64.0.0 ๅˆฐ 100.127.255.255
  • 172.16.0.0 ๅˆฐ 172.31.255.255
  • ::1 ๅ’Œ 0:0:0:0:0:0:0:1 (IPv6 ๅ›ž็Žฏๅœฐๅ€)

ๅ› ไธบ Java ไธ€่ˆฌๆ˜ฏๅŽ็ซฏ็ฝ‘็ปœ๏ผŒๅ‰้ขไธ€่ˆฌๆ˜ฏ NGINX ็ญ‰็ญ‰ PROXY .

public XForwardedRequest(HttpServletRequest request) {  
    super(request);  
    this.localName = request.getLocalName();  
    this.localPort = request.getLocalPort();  
    this.remoteAddr = request.getRemoteAddr();  
    this.remoteHost = request.getRemoteHost();  
    this.scheme = request.getScheme();  
    this.secure = request.isSecure();  
    this.serverName = request.getServerName();  
    this.serverPort = request.getServerPort();  
  
    headers = new HashMap<>();  
    for (Enumeration<String> headerNames = request.getHeaderNames(); headerNames.hasMoreElements();) {  
        String header = headerNames.nextElement();  
        headers.put(header, Collections.list(request.getHeaders(header)));  
    }  
}
  • request ไฟกๆฏๅคๅˆถ

2-2 ่งฃๆž X-Forwarded-For ๅคด

for (idx = remoteIpHeaderValue.length - 1; idx >= 0; idx--) {
ย  ย  String currentRemoteIp = remoteIpHeaderValue[idx];
ย  ย  remoteIp = currentRemoteIp;
ย  ย  if (internalProxies != null && internalProxies.matcher(currentRemoteIp).matches()) {
ย  ย  ย  ย  // ๅ†…้ƒจไปฃ็† IP๏ผŒ็ปง็ปญๅ‘ๅทฆๆŸฅๆ‰พ
ย  ย  } else if (trustedProxies != null && trustedProxies.matcher(currentRemoteIp).matches()) {
ย  ย  ย  ย  // ๅฏไฟกไปฃ็† IP๏ผŒๆทปๅŠ ๅˆฐไปฃ็†้“พไธญ
ย  ย  ย  ย  proxiesHeaderValue.addFirst(currentRemoteIp);
ย  ย  } else {
 
ย  ย  ย  ย  // ๆ‰พๅˆฐ็ฌฌไธ€ไธช้žๅ†…้ƒจ้žๅฏไฟก็š„ IP๏ผŒ่ฎคไธบๆ˜ฏๅฎขๆˆท็ซฏ IP
ย  ย  ย  ย  break;
ย  ย  }
}

ไปŽๅณๅ‘ๅทฆ้ๅކ๏ผŒๆ‰พๅˆฐ็ฌฌไธ€ไธช ้žๅ†…้ƒจ็š„ IP

2-3 ่งฃๆž X-Forwarded-By ๅคด

if (remoteIp != null) {  
  
    xRequest.setRemoteAddr(remoteIp);  
    if (getEnableLookups()) {  
        // This isn't a lazy lookup but that would be a little more  
        // invasive - mainly in XForwardedRequest - and if        // enableLookups is true is seems reasonable that the        // hostname will be required so look it up here.        try {  
            InetAddress inetAddress = InetAddress.getByName(remoteIp);  
            // We know we need a DNS look up so use getCanonicalHostName()  
            xRequest.setRemoteHost(inetAddress.getCanonicalHostName());  
        } catch (UnknownHostException e) {  
            log.debug(sm.getString("remoteIpFilter.invalidRemoteAddress", remoteIp), e);  
            xRequest.setRemoteHost(remoteIp);  
        }  
    } else {  
        xRequest.setRemoteHost(remoteIp);  
    }  
  
    if (proxiesHeaderValue.size() == 0) {  
        xRequest.removeHeader(proxiesHeader);  
    } else {  
        String commaDelimitedListOfProxies = StringUtils.join(proxiesHeaderValue);  
        xRequest.setHeader(proxiesHeader, commaDelimitedListOfProxies);  
    }  
    if (newRemoteIpHeaderValue.size() == 0) {  
        xRequest.removeHeader(remoteIpHeader);  
    } else {  
        String commaDelimitedRemoteIpHeaderValue = StringUtils.join(newRemoteIpHeaderValue);  
        xRequest.setHeader(remoteIpHeader, commaDelimitedRemoteIpHeaderValue);  
    }  
}

2-4 ่งฃๆž protocolHeader ๅคด

if (protocolHeader != null) {  
    String protocolHeaderValue = request.getHeader(protocolHeader);  
    if (protocolHeaderValue == null) {  
        // Don't modify the secure, scheme and serverPort attributes  
        // of the request    } else if (isForwardedProtoHeaderValueSecure(protocolHeaderValue)) {  
        xRequest.setSecure(true);  
        xRequest.setScheme("https");  
        setPorts(xRequest, httpsServerPort);  
    } else {  
        xRequest.setSecure(false);  
        xRequest.setScheme("http");  
        setPorts(xRequest, httpServerPort);  
    }  
}
  • ๆ นๆฎ่ฟ™ไธชๅคด๏ผŒ้‡ๆ–ฐ่ฎพ็ฝฎ http ่ฟ˜ๆ˜ฏ https

2-5 ่งฃๆž hostHeader

if (hostHeader != null) {  
    String hostHeaderValue = request.getHeader(hostHeader);  
    if (hostHeaderValue != null) {  
        try {  
            int portIndex = Host.parse(hostHeaderValue);  
            if (portIndex > -1) {  
                log.debug(sm.getString("remoteIpFilter.invalidHostWithPort", hostHeaderValue, hostHeader));  
                hostHeaderValue = hostHeaderValue.substring(0, portIndex);  
            }  
  
            xRequest.setServerName(hostHeaderValue);  
            if (isChangeLocalName()) {  
                xRequest.setLocalName(hostHeaderValue);  
            }  
  
        } catch (IllegalArgumentException iae) {  
            log.debug(sm.getString("remoteIpFilter.invalidHostHeader", hostHeaderValue, hostHeader));  
        }  
    }  
}

3-Conclusion

้€š่ฟ‡ Wrapper ็š„ๆ–นๅผๅŽปๆžๅฎš . X-Forwarded-Proto X-Forwarded-Host ๅ’Œ X-Forwarded-For ๅคด .

่€ƒ่™‘ไบ† ๅ†…็ฝ‘็ฝ‘ๆฎต, HTTPS ็ญ‰็ญ‰ๆ–นๅผ๏ผŒ็›ธๅฏนๆฏ”่พƒๅฎŒๅ–„