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