Zuul處理Cookie和重定向詳解 一點課堂(多岸學院)

本文將幫助你們分析問題緣由並給出解決這兩個常見問題的方法。

會話保持問題 經過跟蹤一個HTTP請求通過Zuul到具體服務,再到返回結果的全過程。咱們很容易就能發現,在傳遞的過程當中,HTTP請求頭信息中的Cookie和Authorization都沒有被正確地傳遞給具體服務,因此最終致使會話狀態沒有獲得保持的現象。 那麼這些信息是在哪裏丟失的呢?咱們從Zuul進行路由轉發的過濾器做爲起點,來一探究竟。下面是RibbonRoutingFilter過濾器的實現片斷:瀏覽器

public class RibbonRoutingFilter extends ZuulFilter{
	...
	protected ProxyRequestHelper helper;
	
	[@Override](https://my.oschina.net/u/1162528)
	public Object run() {
		RequestContext context = RequestContext.getCurrentContext();
		this.helper.addIgnoredHeaders();
		try {
			RibbonCommandContext commandContext = buildCommandContext(context);
			ClientHttpResponse response = forward(commandContext);
			setResponse(response);
			return response;
		}
		...
		return null;
	}
	
		protected RibbonCommandContext buildCommandContext(RequestContext context) {
		HttpServletRequest request = context.getRequest();
		MultiValueMap<String, String> headers = this.helper
				.buildZuulRequestHeaders(request);
		MultiValueMap<String, String> params = this.helper
				.buildZuulRequestQueryParams(request);
		...
	}
}

這裏有三個重要元素:ide

  • 過濾器的核心邏輯run函數實現,其中調用了內部函數buildCommandContext來構建上下文內容
  • 而buildCommandContext中調用了helper對象的buildZuulRequestHeaders方法來處理請求頭信息
  • helper對象是ProxyRequestHelper類的實例

接下來咱們再看看ProxyRequestHelper的實現:函數

public class ProxyRequestHelper {
	public MultiValueMap<String, String> buildZuulRequestHeaders(
			HttpServletRequest request) {
		RequestContext context = RequestContext.getCurrentContext();
		MultiValueMap<String, String> headers = new HttpHeaders();
		Enumeration<String> headerNames = request.getHeaderNames();
		if (headerNames != null) {
			while (headerNames.hasMoreElements()) {
				String name = headerNames.nextElement();
				if (isIncludedHeader(name)) {
					Enumeration<String> values = request.getHeaders(name);
					while (values.hasMoreElements()) {
						String value = values.nextElement();
						headers.add(name, value);
					}
				}
			}
		}
		Map<String, String> zuulRequestHeaders = context.getZuulRequestHeaders();
		for (String header : zuulRequestHeaders.keySet()) {
			headers.set(header, zuulRequestHeaders.get(header));
		}
		headers.set(HttpHeaders.ACCEPT_ENCODING, "gzip");
		return headers;
	}
	public boolean isIncludedHeader(String headerName) {
		String name = headerName.toLowerCase();
		RequestContext ctx = RequestContext.getCurrentContext();
		if (ctx.containsKey(IGNORED_HEADERS)) {
			Object object = ctx.get(IGNORED_HEADERS);
			if (object instanceof Collection && ((Collection<?>) object).contains(name)) {
				return false;
			}
		}
		...
	}
}

從上述源碼中,咱們能夠看到構建頭信息的方法buildZuulRequestHeaders經過isIncludedHeader函數來判斷當前請求的各個頭信息是否在忽略的頭信息清單中,若是是的話就不組織到這次轉發的請求中去。那麼這些須要忽略的頭信息是在哪裏初始化的呢?在PRE階段的PreDecorationFilter過濾器中,咱們能夠找到答案:學習

public class PreDecorationFilter extends ZuulFilter{
	...
	public Object run() {
		RequestContext ctx = RequestContext.getCurrentContext();
		final String requestURI = this.urlPathHelper.getPathWithinApplication(ctx.getRequest());
		Route route = this.routeLocator.getMatchingRoute(requestURI);
		if (route != null) {
			String location = route.getLocation();
			if (location != null) {
				ctx.put("requestURI", route.getPath());
				ctx.put("proxy", route.getId());
             	// 處理忽略頭信息的部分
				if (!route.isCustomSensitiveHeaders()) {
					this.proxyRequestHelper.addIgnoredHeaders(
						this.properties.getSensitiveHeaders()
						.toArray(new String[0]));
				} else {
					this.proxyRequestHelper.addIgnoredHeaders(
						route.getSensitiveHeaders()
						.toArray(new String[0]));
				}
		...
}

從上述源碼中,咱們能夠看到有一段if/else塊,經過調用ProxyRequestHelper的addIgnoredHeaders方法來添加須要忽略的信息到請求上下文中,供後續ROUTE階段的過濾器使用。這裏的if/else塊分別用來處理全局設置的敏感頭信息和指定路由設置的敏感頭信息。而全局的敏感頭信息定義於ZuulProperties中:網站

[@Data](https://my.oschina.net/difrik)
@ConfigurationProperties("zuul")
public class ZuulProperties{
	private Set<String> sensitiveHeaders = new LinkedHashSet<>(
			Arrays.asList("Cookie", "Set-Cookie", "Authorization"));
	...
}

因此解決該問題的思路也很簡單,咱們只須要經過設置sensitiveHeaders便可,設置方法分爲兩種: 全局設置:ui

  • zuul.sensitive-headers= 指定路由設置:
  • zuul.routes.<routeName>.sensitive-headers=
  • zuul.routes.<routeName>.custom-sensitive-headers=true

重定向問題

在使用Spring Cloud Zuul對接Web網站的時候,處理完了會話控制問題以後。每每咱們還會碰到以下圖所示的問題,咱們在瀏覽器中經過Zuul發起了登陸請求,該請求會被路由到某WebSite服務,該服務在完成了登陸處理以後,會進行重定向到某個主頁或歡迎頁面。此時,仔細的開發者會發現,在登陸完成以後,咱們瀏覽器中URL的HOST部分發生的改變,該地址變成了具體WebSite服務的地址了。這就是在這一節,咱們將分析和解決的重定向問題!this

在這裏插入圖片描述

出現該問題的根源是Spring Cloud Zuul沒有正確的處理HTTP請求頭信息中的Host致使。在Brixton版本中,Spring Cloud Zuul的PreDecorationFilter過濾器實現時徹底沒有考慮這一問題,它更多的定位於REST API的網關。因此若是要在Brixton版本中增長這一特性就相對較爲複雜,不過好在Camden版本以後,Spring Cloud Netflix 1.2.x版本的Zuul加強了該功能,咱們只須要經過配置屬性zuul.add-host-header=true就能讓本來有問題的重定向操做獲得正確的處理。關於更多Host頭信息的處理,讀者能夠參考本文以前的分析思路,能夠經過查看PreDecorationFilter過濾器的源碼來詳細更多實現細節。 在這裏插入圖片描述 QQ討論羣組:984370849 706564342 歡迎加入討論url

想要深刻學習的同窗們能夠加入QQ羣討論,有全套資源分享,經驗探討,沒錯,咱們等着你,分享互相的故事! 在這裏插入圖片描述.net

相關文章
相關標籤/搜索