阿里云slb配置https重定向后变为http

背景描述

问题

  1. 阿里云slb配置443端口监听,然后将80端口的监听配置为重定向到https:443端口。

  2. 通过http://abc.com来访问站点,成功跳转至https://abc.com,实现了http强制跳https。

  3. 输入账号、密码登录系统,然后 “500”错误,F12浏览器debug发现提示以下错误:

Mixed Content: The page at 'https://abc.com' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://abc.com/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框架,里面有什么loginUrlsucessUrl之类的配置,这里也会利用redirect进行跳转,具体方案如下:

  1. 重写RedirectView类,将http10Compatible开关关闭;
  2. 硬编码强制修改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 scheme
            String 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)://abc.com,然后发起重定向。

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.html
		if (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);
			//303
			response.setStatus(statusCode.value());
			response.setHeader("Location", encodedURL);
		}
	}

如果redirectHttp10Compatible=true,就把重定向的targetUrl的组装工作交给了servlet 容器处理,容器肯定是按照Servlet规范做了。
如果redirectHttp10Compatible=false,响应码是303,并且Location的值是一个相对地址。猜测是浏览器收到这个相对地址以后会自动拼接前面的http(s)://abc.com,然后发起重定向。

总结

到这里问题基本清楚了,来个总结吧。

Http10CompatibleHttp10 No Compatible
Status Code:200
Location:"${request.scheme}:\//domain.com/targetPath.html"
Status Code:303
Location:"/targetPath.html"

结论很明显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采用相应的协议。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值