阿里雲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,然後發起重定向。

總結

到這裏問題基本清楚了,來個總結吧。

Http10Compatible Http10 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採用相應的協議。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章