Nginx SSL+tomcat cluster, fetch the correct protocol for https

Keywords: Tomcat Nginx SSL Apache

Nginx SSL+tomcat cluster, request.getScheme() fetches the correct protocol for https

 

Recently, in a project, Nginx +tomcat cluster is used in the architecture, and SSL,tomcat no SSL are configured under nginx. The project uses https protocol.

However, it is clearly an https url request that finds the log s.


    0428 15:55:55 INFO  (PaymentInterceptor.java:44) preHandle() - requestStringForLog:    {  
            "request.getRequestURL():": "http://trade.feilong.com/payment/paymentChannel?id=212&s=a84485e0985afe97fffd7fd7741c93851d83a4f6",  
            "request.getMethod:": "GET",  
            "_parameterMap":         {  
                "id": ["212"],  
                "s": ["a84485e0985afe97fffd7fd7741c93851d83a4f6"]  
            }  
        }  

The request. getRequest URL () always outputs http://trade.feilong.com/payment/paymentChannel? Id=212&s=a84485e0985afe97fffd7fd7741c93851d83a4f6

But the URL in the browser is https://trade.feilong.com/payment/paymentChannel?Id=212&s=a84485e0985afe97fffd7fd7741c93851d83a4f6.

Instantaneous subversion of my Java viewThe API is very clear:

getRequestURL():

Reconstructs the URL the client used to make the request. The returned URL contains a protocol, server name, port number, and server path, but it does not include query string parameters.  

That is to say, getRequestURL() outputs the path without query string (including protocol port server path, etc.).

 

 

Moreover, it was found that

request.getScheme()  //Always http, not actual HTTP or https  
request.isSecure()  //Always false (because always http)  
request.getRemoteAddr()  //Always the IP requested by nginx, not the user's IP  
request.getRequestURL()  //Always the URL requested by nginx, not the URL actually requested by the user  
response.sendRedirect( Relative url )  //Always redirect to http (because you think the current HTTP request is) 

After consulting some information, we found a solution:

The solution is simple, just configure Nginx and Tomcat separately, without changing the program.

Configure Nginx forwarding options:


    proxy_set_header       Host $host;  
    proxy_set_header  X-Real-IP  $remote_addr;  
    proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;  
    proxy_set_header X-Forwarded-Proto  $scheme;  

proxy_set_header X-Forwarded-Proto $scheme;

Configure a Valve under the Engine module of Tomcat server.xml:


    <Valve className="org.apache.catalina.valves.RemoteIpValve"  
    remoteIpHeader="X-Forwarded-For"  
    protocolHeader="X-Forwarded-Proto"  
    protocolHeaderHttpsValue="https"/>  

The purpose of configuring X-Forwarded-Proto is to correctly identify whether the protocol issued by the actual user is http or https.

The above five tests turn out to be the right results, just like the user visiting Tomcat directly.

 

For RemoteIpValve, interested students can read doc

http://tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalina/valves/RemoteIpValve.html


    Tomcat port of mod_remoteip, this valve replaces the apparent client remote IP address and hostname for the request with the IP address list presented by a proxy or a load balancer via a request headers (e.g. "X-Forwarded-For").   
       
    Another feature of this valve is to replace the apparent scheme (http/https) and server port with the scheme presented by a proxy or a load balancer via a request header (e.g. "X-Forwarded-Proto").   

 

Looking at their source code, it's relatively simple. In front of various frameworks and algorithms, this class has little impact on performance.

 

  • If you do not configure the protocolHeader property, do nothing.
  • If you configure the protocolHeader, but request.getHeader(protocolHeader) takes out a value of null and does nothing.
  • If the protocolHeader is configured, but the value taken out by request. getHeader (which ignores case) is the configured protocolHeader HttpsValue (default https),scheme is set to https, and port is set to HTTPS ServerPort.
  • Other settings are http
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 (protocolHeaderHttpsValue.equalsIgnoreCase(protocolHeaderValue)) {  
            request.setSecure(true);  
            // use request.coyoteRequest.scheme instead of request.setScheme() because request.setScheme() is no-op in Tomcat 6.0  
            request.getCoyoteRequest().scheme().setString("https");  
              
            request.setServerPort(httpsServerPort);  
        } else {  
            request.setSecure(false);  
            // use request.coyoteRequest.scheme instead of request.setScheme() because request.setScheme() is no-op in Tomcat 6.0  
            request.getCoyoteRequest().scheme().setString("http");  
              
            request.setServerPort(httpServerPort);  
        }  
    }  

Posted by BoxingKing on Thu, 16 May 2019 00:45:56 -0700