天天看點

阿裡雲slb配置https重定向後變為http背景描述網上搜尋到的方案原理剖析總結最佳實踐

阿裡雲slb配置https重定向後變http問題解決

  • 背景描述
    • 問題
    • 部署結構
  • 網上搜尋到的方案
      • 方案一
      • 方案二
  • 原理剖析
    • Servlet容器重定向
    • Shiro 重定向
    • Spring MVC 重定向
  • 總結
  • 最佳實踐

背景描述

問題

  1. 阿裡雲slb配置443端口監聽,然後将80端口的監聽配置為重定向到https:443端口。
  2. 通過http://abc.com來通路站點,成功跳轉至https://abc.com,實作了http強制跳https。
  3. 輸入賬号、密碼登入系統,然後 “500”錯誤,F12浏覽器debug發現提示以下錯誤:

部署結構

阿裡雲slb配置https重定向後變為http背景描述網上搜尋到的方案原理剖析總結最佳實踐

網上搜尋到的方案

方案一

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進行跳轉,具體方案如下:

  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采用相應的協定。