Spring Cloud实战小贴士:Zuul处理Cookie和重定向

由于我们在之前所有的入门教程中,对于HTTP请求都采用了简单的接口实现。而实际使用过程中,我们的HTTP请求要复杂的多,本文主要分析HTTP请求时遇见的常见问题以及解决方法。 2017-05-02 23:05:44 HTTPZuulCookie 前端开发JS:事件循环机制、调用栈以及任务队列 在之前,我只是简单地认为由于函数执行很快,setTimeout执行时间即便为0也不会马上输出,而是等待函数执行完后再输出。实际上其运行机制就是js中的事件循环机制,在这个循环机制中呢,又与call Stack和task queue有关。 2017-05-02 22:38:44 前端开发JS事件循环机制 白话阿里巴巴Java开发手册(编程规约) 阿里巴巴发布了《阿里巴巴Java开发手册》,总结了阿里人多年一线实战中积累的研发流程规范,这些流程规范在一定程度上能够保证最终的项目交付质量,通过限制开发人员的编程风格、实现方式来避免研发人员在实践中容易犯的错误,同样的问题大家使用同样的模式解决,便于后期维护和扩展,确保最终在大规模协作的项目中达成既定目标。 2017-05-02 21:14:20 阿里巴巴Java开发 关于高效开发的一些套路与实践 一个好的代码结构是需要需求分析,架构设计做为辅助的,Stay尝试向你描述一个理想高效的工作流程,有了这个套路,不仅能让你缩短编码时间,还能得到团队的认可。

由于我们在之前所有的入门教程中,对于HTTP请求都采用了简单的接口实现。而实际使用过程中,我们的HTTP请求要复杂的多,本文主要分析HTTP请求时遇见的常见问题以及解决方法。

由于我们在之前所有的入门教程中,对于HTTP请求都采用了简单的接口实现。而实际使用过程中,我们的HTTP请求要复杂的多,比如当我们将Spring Cloud Zuul作为API网关接入网站类应用时,往往都会碰到下面这两个非常常见的问题:

  • 会话无法保持
  • 重定向后的HOST错误

本文将帮助大家分析问题原因并给出解决这两个常见问题的方法。

[[190127]]

一、会话保持问题

通过跟踪一个HTTP请求经过Zuul到具体服务,再到返回结果的全过程。我们很容易就能发现,在传递的过程中,HTTP请求头信息中的Cookie和Authorization都没有被正确地传递给具体服务,所以最终导致会话状态没有得到保持的现象。

那么这些信息是在哪里丢失的呢?我们从Zuul进行路由转发的过滤器作为起点,来一探究竟。下面是RibbonRoutingFilter过滤器的实现片段:

  1. publicclassRibbonRoutingFilterextendsZuulFilter{
  2. ...
  3. protectedProxyRequestHelperhelper;
  4. @Override
  5. publicObjectrun(){
  6. RequestContextcontext=RequestContext.getCurrentContext();
  7. this.helper.addIgnoredHeaders();
  8. try{
  9. RibbonCommandContextcommandContext=buildCommandContext(context);
  10. ClientHttpResponseresponse=forward(commandContext);
  11. setResponse(response);
  12. returnresponse;
  13. }
  14. ...
  15. returnnull;
  16. }
  17. protectedRibbonCommandContextbuildCommandContext(RequestContextcontext){
  18. HttpServletRequestrequest=context.getRequest();
  19. MultiValueMap<String,String>headers=this.helper
  20. .buildZuulRequestHeaders(request);
  21. MultiValueMap<String,String>params=this.helper
  22. .buildZuulRequestQueryParams(request);
  23. ...
  24. }
  25. }

这里有三个重要元素:

  • 过滤器的核心逻辑run函数实现,其中调用了内部函数buildCommandContext来构建上下文内容
  • 而buildCommandContext中调用了helper对象的buildZuulRequestHeaders方法来处理请求头信息
  • helper对象是ProxyRequestHelper类的实例

接下来我们再看看ProxyRequestHelper的实现:

  1. publicclassProxyRequestHelper{
  2. publicMultiValueMap<String,String>buildZuulRequestHeaders(
  3. HttpServletRequestrequest){
  4. RequestContextcontext=RequestContext.getCurrentContext();
  5. MultiValueMap<String,String>headers=newHttpHeaders();
  6. Enumeration<String>headerNames=request.getHeaderNames();
  7. if(headerNames!=null){
  8. while(headerNames.hasMoreElements()){
  9. Stringname=headerNames.nextElement();
  10. if(isIncludedHeader(name)){
  11. Enumeration<String>values=request.getHeaders(name);
  12. while(values.hasMoreElements()){
  13. Stringvalue=values.nextElement();
  14. headers.add(name,value);
  15. }
  16. }
  17. }
  18. }
  19. Map<String,String>zuulRequestHeaders=context.getZuulRequestHeaders();
  20. for(Stringheader:zuulRequestHeaders.keySet()){
  21. headers.set(header,zuulRequestHeaders.get(header));
  22. }
  23. headers.set(HttpHeaders.ACCEPT_ENCODING,"gzip");
  24. returnheaders;
  25. }
  26. publicbooleanisIncludedHeader(StringheaderName){
  27. Stringname=headerName.toLowerCase();
  28. RequestContextctx=RequestContext.getCurrentContext();
  29. if(ctx.containsKey(IGNORED_HEADERS)){
  30. Objectobject=ctx.get(IGNORED_HEADERS);
  31. if(objectinstanceofCollection&&((Collection<?>)object).contains(name)){
  32. returnfalse;
  33. }
  34. }
  35. ...
  36. }
  37. }

从上述源码中,我们可以看到构建头信息的方法buildZuulRequestHeaders通过isIncludedHeader函数来判断当前请求的各个头信息是否在忽略的头信息清单中,如果是的话就不组织到此次转发的请求中去。那么这些需要忽略的头信息是在哪里初始化的呢?在PRE阶段的PreDecorationFilter过滤器中,我们可以找到答案:

  1. publicclassPreDecorationFilterextendsZuulFilter{
  2. ...
  3. publicObjectrun(){
  4. RequestContextctx=RequestContext.getCurrentContext();
  5. finalStringrequestURI=this.urlPathHelper.getPathWithinApplication(ctx.getRequest());
  6. Routeroute=this.routeLocator.getMatchingRoute(requestURI);
  7. if(route!=null){
  8. Stringlocation=route.getLocation();
  9. if(location!=null){
  10. ctx.put("requestURI",route.getPath());
  11. ctx.put("proxy",route.getId());
  12. //处理忽略头信息的部分
  13. if(!route.isCustomSensitiveHeaders()){
  14. this.proxyRequestHelper.addIgnoredHeaders(
  15. this.properties.getSensitiveHeaders()
  16. .toArray(newString[0]));
  17. }else{
  18. this.proxyRequestHelper.addIgnoredHeaders(
  19. route.getSensitiveHeaders()
  20. .toArray(newString[0]));
  21. }
  22. ...
  23. }

从上述源码中,我们可以看到有一段if/else块,通过调用ProxyRequestHelper的addIgnoredHeaders方法来添加需要忽略的信息到请求上下文中,供后续ROUTE阶段的过滤器使用。这里的if/else块分别用来处理全局设置的敏感头信息和指定路由设置的敏感头信息。而全局的敏感头信息定义于ZuulProperties中:

  1. @Data
  2. @ConfigurationProperties("zuul")
  3. publicclassZuulProperties{
  4. privateSet<String>sensitiveHeaders=newLinkedHashSet<>(
  5. Arrays.asList("Cookie","Set-Cookie","Authorization"));
  6. ...
  7. }

所以解决该问题的思路也很简单,我们只需要通过设置sensitiveHeaders即可,设置方法分为两种:

1. 全局设置:

  • zuul.sensitive-headers=

2. 指定路由设置:

  • zuul.routes..sensitive-headers=
  • zuul.routes..custom-sensitive-headers=true

二、重定向问题

在使用Spring Cloud Zuul对接Web网站的时候,处理完了会话控制问题之后。往往我们还会碰到如下图所示的问题,我们在浏览器中通过Zuul发起了登录请求,该请求会被路由到某WebSite服务,该服务在完成了登录处理之后,会进行重定向到某个主页或欢迎页面。此时,仔细的开发者会发现,在登录完成之后,我们浏览器中URL的HOST部分发生的改变,该地址变成了具体WebSite服务的地址了。这就是在这一节,我们将分析和解决的重定向问题!

Spring Cloud Zuul对接Web网站

出现该问题的根源是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:1074760229)。转载请注明出处:清一色财经

(0)
打赏 微信扫码打赏 微信扫码打赏 支付宝扫码打赏 支付宝扫码打赏
清一色的头像清一色管理团队
上一篇 2023年5月4日 22:58
下一篇 2023年5月4日 22:58

相关推荐

发表评论

登录后才能评论

联系我们

在线咨询:1643011589-QQbutton

手机:13798586780

QQ/微信:1074760229

QQ群:551893940

工作时间:工作日9:00-18:00,节假日休息

关注微信