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

相关文章
相关标签/搜索