Requ. getRequest URL () gets the server address, nginx reverse proxy host configuration, and the use of tomcat embedded in spring boot

Posted by centipede on Tue, 08 Oct 2019 17:21:22 +0200

tomcat uses the nginx reverse proxy, and the server path obtained becomes the intranet address configured in nginx. If on the same server, it becomes 127.0.0.1 or localhost. What we need is the extranet address. At this time, we need to enable the forwarding request header configuration.
 
How to enable it? Next, look at the source code to find out how to solve it.
Many people will emphasize to look at the source code, if there is no purpose to look directly at the source code is boring, it is difficult to adhere to (God skipped), there will not be much gain. If we encounter problems, most of the time is one hundred degrees online, configuration or code copy and paste, then test no problem, but if there is little information on the Internet, or almost no solution, at this time, we look at the source code, find the corresponding implementation, and sometimes find the problem directly, I think this time is the source of reading. Code the best time, make good use of the search function of eclipse or intellij IDEA, you can generally find the corresponding source code
 
Let's first look at the spring boot tomcat source code. Tomcat WebServerFactoryCustomizer class
private void  customizeRemoteIpValve(ConfigurableTomcatWebServerFactory  factory) {
          Tomcat tomcatProperties =  this.serverProperties.getTomcat();
          String protocolHeader =  tomcatProperties.getProtocolHeader();
          String remoteIpHeader =  tomcatProperties.getRemoteIpHeader();
          // For back compatibility the valve is also  enabled if protocol-header is set
          if (StringUtils.hasText(protocolHeader) ||  StringUtils.hasText(remoteIpHeader)
                   || getOrDeduceUseForwardHeaders()) {
              RemoteIpValve valve = new RemoteIpValve();
              valve.setProtocolHeader(StringUtils.hasLength(protocolHeader) ? protocolHeader
                        : "X-Forwarded-Proto");
              if (StringUtils.hasLength(remoteIpHeader))  {
                   valve.setRemoteIpHeader(remoteIpHeader);
              }
              // The internal proxies default to a white  list of "safe" internal IP
              // addresses
              valve.setInternalProxies(tomcatProperties.getInternalProxies());
              valve.setPortHeader(tomcatProperties.getPortHeader());
              valve.setProtocolHeaderHttpsValue(
                        tomcatProperties.getProtocolHeaderHttpsValue());
              // ... so it's safe to add this valve by  default.
              factory.addEngineValves(valve);
          }
     }

 

 
As you can see, RemoteIpValve is enabled as long as either of the protocolHeader and remoteIpHeader has value
 
spring boot corresponds to the configuration item in the Tomcat class:
server.use-forward-headers=true
server.tomcat.remote-ip-header=x-forwarded-for
server.tomcat.protocol-header=X-Forwarded-Proto

 

 
The implementation of getting the server address is in the invoke method of the RemoteIpValve class
 
In general, if RemoteIpValve is enabled, the specified request header is obtained, the values of RemoteAddr and Scheme s are updated, and the http service transferred by https is represented by security.
 
Then we need to configure the request address of our service in nginx, which is the host request header.
 
Corresponding nginx configuration items:
proxy_set_header   Host             $host:$server_port;
            
proxy_set_header   X-Real-IP        $remote_addr;
proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
proxy_set_header   X-Forwarded-Port  $server_port;
proxy_set_header   X-Forwarded-Proto  $scheme;

 

In this way, we can get the server address we specified in nginx by using the request. getRequest URL () method directly in the back-end service. If we do not use nginx, this problem will not exist, so the back-end service does not need to consider whether the service uses reverse proxy or not.
 
  public void invoke(Request request, Response response)  throws IOException, ServletException {
        final String originalRemoteAddr =  request.getRemoteAddr();
        final String originalRemoteHost =  request.getRemoteHost();
        final String originalScheme = request.getScheme();
        final boolean originalSecure = request.isSecure();
        final int originalServerPort =  request.getServerPort();
        final String originalProxiesHeader =  request.getHeader(proxiesHeader);
        final String originalRemoteIpHeader =  request.getHeader(remoteIpHeader);
        boolean isInternal = internalProxies != null &&
                 internalProxies.matcher(originalRemoteAddr).matches();
        if (isInternal || (trustedProxies != null &&
                 trustedProxies.matcher(originalRemoteAddr).matches())) {
            String remoteIp = null;
            // In java 6, proxiesHeaderValue should be  declared as a java.util.Deque
            LinkedList<String> proxiesHeaderValue = new  LinkedList<>();
            StringBuilder concatRemoteIpHeaderValue = new  StringBuilder();
            for (Enumeration<String> e =  request.getHeaders(remoteIpHeader); e.hasMoreElements();)  {
                if (concatRemoteIpHeaderValue.length() > 0)  {
                    concatRemoteIpHeaderValue.append(", ");
                }
                 concatRemoteIpHeaderValue.append(e.nextElement());
            }
            String[] remoteIpHeaderValue =  commaDelimitedListToStringArray(concatRemoteIpHeaderValue.toString());
            int idx;
            if (!isInternal) {
                 proxiesHeaderValue.addFirst(originalRemoteAddr);
            }
            // loop on remoteIpHeaderValue to find the  first trusted remote ip and to build the proxies chain
            for (idx = remoteIpHeaderValue.length - 1; idx  >= 0; idx--) {
                String currentRemoteIp =  remoteIpHeaderValue[idx];
                remoteIp = currentRemoteIp;
                if (internalProxies !=null &&  internalProxies.matcher(currentRemoteIp).matches()) {
                    // do nothing, internalProxies IPs are  not appended to the
                } else if (trustedProxies != null &&
                         trustedProxies.matcher(currentRemoteIp).matches()) {
                     proxiesHeaderValue.addFirst(currentRemoteIp);
                } else {
                    idx--; // decrement idx because break  statement doesn't do it
                    break;
                }
            }
            // continue to loop on remoteIpHeaderValue to  build the new value of the remoteIpHeader
            LinkedList<String> newRemoteIpHeaderValue =  new LinkedList<>();
            for (; idx >= 0; idx--) {
                String currentRemoteIp =  remoteIpHeaderValue[idx];
                 newRemoteIpHeaderValue.addFirst(currentRemoteIp);
            }
            if (remoteIp != null) {
                request.setRemoteAddr(remoteIp);
                request.setRemoteHost(remoteIp);
                if (proxiesHeaderValue.size() == 0) {
                     request.getCoyoteRequest().getMimeHeaders().removeHeader(proxiesHeader);
                } else {
                    String commaDelimitedListOfProxies =  listToCommaDelimitedString(proxiesHeaderValue);
                     request.getCoyoteRequest().getMimeHeaders().setValue(proxiesHeader).setString(commaDelimitedListOfProxies);
                }
                if (newRemoteIpHeaderValue.size() == 0) {
                     request.getCoyoteRequest().getMimeHeaders().removeHeader(remoteIpHeader);
                } else {
                    String  commaDelimitedRemoteIpHeaderValue =  listToCommaDelimitedString(newRemoteIpHeaderValue);
                     request.getCoyoteRequest().getMimeHeaders().setValue(remoteIpHeader).setString(commaDelimitedRemoteIpHeaderValue);
                }
            }
            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))  {
                    request.setSecure(true);
                     request.getCoyoteRequest().scheme().setString("https");
                    setPorts(request, httpsServerPort);
                } else {
                    request.setSecure(false);
                     request.getCoyoteRequest().scheme().setString("http");
                    setPorts(request, httpServerPort);
                }
            }
            if (log.isDebugEnabled()) {
                log.debug("Incoming request " +  request.getRequestURI() + " with originalRemoteAddr '" +  originalRemoteAddr
                          + "', originalRemoteHost='" +  originalRemoteHost + "', originalSecure='" +  originalSecure + "', originalScheme='"
                          + originalScheme + "' will be seen  as newRemoteAddr='" + request.getRemoteAddr() + "',  newRemoteHost='"
                          + request.getRemoteHost() + "',  newScheme='" + request.getScheme() + "', newSecure='" +  request.isSecure() + "'");
            }
        } else {
            if (log.isDebugEnabled()) {
                log.debug("Skip RemoteIpValve for request "  + request.getRequestURI() + " with originalRemoteAddr '"
                        + request.getRemoteAddr() + "'");
            }
        }
        if (requestAttributesEnabled) {
             request.setAttribute(AccessLog.REMOTE_ADDR_ATTRIBUTE,
                    request.getRemoteAddr());
             request.setAttribute(Globals.REMOTE_ADDR_ATTRIBUTE,
                    request.getRemoteAddr());
             request.setAttribute(AccessLog.REMOTE_HOST_ATTRIBUTE,
                    request.getRemoteHost());
             request.setAttribute(AccessLog.PROTOCOL_ATTRIBUTE,
                    request.getProtocol());
             request.setAttribute(AccessLog.SERVER_PORT_ATTRIBUTE,
                     Integer.valueOf(request.getServerPort()));
        }
        try {
            getNext().invoke(request, response);
        } finally {
            request.setRemoteAddr(originalRemoteAddr);
            request.setRemoteHost(originalRemoteHost);
            request.setSecure(originalSecure);
             request.getCoyoteRequest().scheme().setString(originalScheme);
            request.setServerPort(originalServerPort);
            MimeHeaders headers =  request.getCoyoteRequest().getMimeHeaders();
            if (originalProxiesHeader == null ||  originalProxiesHeader.length() == 0) {
                headers.removeHeader(proxiesHeader);
            } else {
                 headers.setValue(proxiesHeader).setString(originalProxiesHeader);
            }
            if (originalRemoteIpHeader == null ||  originalRemoteIpHeader.length() == 0) {
                headers.removeHeader(remoteIpHeader);
            } else {
                 headers.setValue(remoteIpHeader).setString(originalRemoteIpHeader);
            }
        }
    }

 

Topics: Programming Tomcat Nginx Spring Java