通过负载均衡访问服务端导致https重定向到http
后端服务使用spring boot内嵌tomcat,使用阿里云负载均衡做反向代理。负载均衡器上监听https,并配置证书,转发到后端时卸载掉证书使用http。如果代码中重定向到某个相对路径时,tomcat默认会转换成绝对路径,协议就取成了http的方式,发送302到前端时携带的location中是http://开头的,导致负载均衡器直接返回 400 Bad Request The plain HTTP request was sent to HTTPS port 。按道理说这样的问题通常有个可选配置可以处理,我就一层层查代码到了tomcat 的org.apache.catalina.connector.Response类里,看到有条件判断如果true就直接将相对路径发给前端,如果是false就获取绝对路径并发给前端。那问题就出在“条件”上,跟随下代码发现可以通过 在springboot的配置文件中使用 server.tomcat.useRelativeRedirects=true 解决。如果前端请求是http1.1并且 server.tomcat.useRelativeRedirects==true,则发送相对路径,此时浏览器拿到相对路径后进行跳转页面就跟随了浏览器地址栏中的协议了。至此可以发现,其实这个问题查手册文档可能也能找到解决方案。但百度出来的结果却没有合适的方案。
/** * Wrapper object for the Coyote response. * * @author Remy Maucherat * @author Craig R. McClanahan */ public class Response implements HttpServletResponse { //.................. /** * Internal method that allows a redirect to be sent with a status other * than {@link HttpServletResponse#SC_FOUND} (302). No attempt is made to * validate the status code. * * @param location Location URL to redirect to * @param status HTTP status code that will be sent * @throws IOException an IO exception occurred */ public void sendRedirect(String location, int status) throws IOException { if (isCommitted()) { throw new IllegalStateException(sm.getString("coyoteResponse.sendRedirect.ise")); } // Ignore any call from an included servlet if (included) { return; } // Clear any data content that has been buffered resetBuffer(true); // Generate a temporary redirect to the specified location try { String locationUri; // Relative redirects require HTTP/1.1 if (getRequest().getCoyoteRequest().getSupportsRelativeRedirects() && getContext().getUseRelativeRedirects()) { locationUri = location; } else { locationUri = toAbsolute(location); } setStatus(status); setHeader("Location", locationUri); if (getContext().getSendRedirectBody()) { PrintWriter writer = getWriter(); writer.print(sm.getString("coyoteResponse.sendRedirect.note", Escape.htmlElementContent(locationUri))); flushBuffer(); } } catch (IllegalArgumentException e) { log.warn(sm.getString("response.sendRedirectFail", location), e); setStatus(SC_NOT_FOUND); } // Cause the response to be finished (from the application perspective) setSuspended(true); } //................... }