阿里云slb配置https重定向后变http问题解决
背景描述问题部署结构 网上搜索到的方案方案一方案二 原理剖析Servlet容器重定向Shiro 重定向Spring MVC 重定向 总结最佳实践背景描述
问题
阿里云slb配置443端口监听,然后将80端口的监听配置为重定向到https:443端口。
通过来访问站点,成功跳转至,实现了http强制跳https。
输入账号、密码登录系统,然后 “500”错误,F12浏览器debug发现提示以下错误:
Mixed Content: The page at '' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint '/onLoginSuccess.html'. This request has been blocked; the content must be served over HTTPS.
部署结构
网上搜索到的方案
方案一
Spring MVC 里面使用到了redirect:/path
,可以通过以下配置来搞定:
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"><!-- redirectHttp10Compatible:解决https环境下使用redirect重定向地址变为http的协议,无法访问服务的问题,设置为false,即关闭了对http1.0协议的兼容支持--> <property name="redirectHttp10Compatible" value="false" /> </bean>
大概意思就是因为代码了做了对http1.0
兼容,导致了重定向以后返回去的就是http了。
方案二
使用了shiro
框架,里面有什么loginUrl
、sucessUrl
之类的配置,这里也会利用redirect进行跳转,具体方案如下:
重写RedirectView类,将http10Compatible
开关关闭;硬编码强制修改response Header里面的Location的值;
太复杂了,学不会呀!!!
原理剖析
Servlet容器重定向
Servlet容器Tomcat(tomcat-embed-8.5.31)里面的一个类:org.apache.catalina.connector.Response
里面有这么一段代码:
/*** 如果有需要的话把一个相对地址转化为绝对地址(我自己乱翻译的)*/protected String toAbsolute(String location) {...boolean leadingSlash = location.startsWith("/");if (location.startsWith("//")) {// Add the schemeString scheme = request.getScheme();....} else if (leadingSlash || !UriUtil.hasScheme(location)) {String scheme = request.getScheme();...} else {//啥也不干 直接返回return (location);}}
意思就是,需要重定向的话,Location里面的地址的Scheme根据request来,两者保持一致。
Shiro 重定向
属性http10Compatible
的值影响重定向的行为。
org.apache.shiro.web.util.RedirectView
protected void sendRedirect(HttpServletRequest request, HttpServletResponse response, String targetUrl, boolean http10Compatible) throws IOException {if (http10Compatible) {response.sendRedirect(response.encodeRedirectURL(targetUrl));} else {response.setStatus(303);response.setHeader("Location", response.encodeRedirectURL(targetUrl));}}
如果http10Compatible=true
,就把重定向的targetUrl的组装工作交给了servlet 容器处理,容器肯定是按照Servlet规范做了。
如果http10Compatible=false
,响应码是303
,并且Location
的值是一个相对地址。猜测是浏览器收到这个相对地址以后会自动拼接前面的http(s)://
,然后发起重定向。
Spring MVC 重定向
Spring MVC 中控制对Http 1.0 是否兼容的标识位是redirectHttp10Compatible
该属性在UrlBasedViewResolver
视图解析器内。
生效的地方依然是RedirectView
,类:org.springframework.web.servlet.view.RedirectView
相关代码:
protected void sendRedirect(HttpServletRequest request, HttpServletResponse response,String targetUrl, boolean http10Compatible) throws IOException {...//encodedURL /abc/tests/safa.htmlif (http10Compatible) {HttpStatus attributeStatusCode = (HttpStatus) request.getAttribute(View.RESPONSE_STATUS_ATTRIBUTE);if (this.statusCode != null) {response.setStatus(this.statusCode.value());response.setHeader("Location", encodedURL);}else if (attributeStatusCode != null) {response.setStatus(attributeStatusCode.value());response.setHeader("Location", encodedURL);}else {// Send status code 302 by default.response.sendRedirect(encodedURL);}}else {HttpStatus statusCode = getHttp11StatusCode(request, response, targetUrl);//303response.setStatus(statusCode.value());response.setHeader("Location", encodedURL);}}
如果redirectHttp10Compatible=true
,就把重定向的targetUrl的组装工作交给了servlet 容器处理,容器肯定是按照Servlet规范做了。
如果redirectHttp10Compatible=false
,响应码是303
,并且Location
的值是一个相对地址。猜测是浏览器收到这个相对地址以后会自动拼接前面的http(s)://
,然后发起重定向。
总结
到这里问题基本清楚了,来个总结吧。
结论很明显Http10Compatible
兼容与否,其实跟Http、Https并没有关系,只是恰巧出现的303状态码,以及Location里面存放的不在是完整的跳转url,而是一个/
开头的相对地址,协议的组装交给了浏览器处理。因此给大家了这种错觉,Https和Http的问题依然存在。
最佳实践
Tomcat有一个配置项:
public static class Tomcat {/*** Header that holds the incoming protocol, usually named "X-Forwarded-Proto".*/private String protocolHeader;}
当请求通过nginx代理或者阿里云SLB转发的时候通过配置将请求protocol
放在X-Forwarded-Proto
Header 里面。后端容器就会根据协议在生成redirect Location采用相应的协议。