接上篇《​​25.Zuul简介及代码示例​​》  Spring Cloud版本为Finchley.SR2版

上一篇我们简单介绍了Spring Cloud的微服务API网关组件Zuul,并且引入Zuul实现了一个简单的反向代理功能,了解了Zuul的基本配置、大致原理以及相关的路由配置。本篇我们继续了解一下Zuul的其它特性。
本部分官方文档:​​​https://cloud.spring.io/spring-cloud-static/Finchley.SR4/single/spring-cloud.html#_zuul_http_client​​ 注:好像Finchley.SR2的文档已经挂了,最新的是Finchley.SR4的文档。

一、Zuul Http Client

根据官方文档记载,Zuul调用Http服务,使用的默认的Http客户端对象是Apache的HttpClient,代替了之前使用的Ribbon的RestClient。如果需要使用RestClient,或者是okhttp3.OkHttpClient,需要设置ribbon.restclient.enabled=true,亦或是ribbon.okhttp.enabled=true。

二、Zuul有关Head过滤的设置

1、sensitive Headers
我们有一些敏感的Http头(Head),我们不想让Head头中的敏感信息随着HTTP请求的转发而泄露,就需要在路由配置中设置一个忽略Head信息的清单,我们可以在配置文件使用“sensitiveHeaders” 属性进行设置。

我们在上一篇的Zuul工程的application.yml配置文件中添加“sensitive-Headers”配置:

zuul:
routes:
user:
path: /user-url/**
sensitive-headers: Cookie,Set-Cookie,Authorization
url: http://192.168.3.1:7900

这里我们设置过滤的敏感头为“Cookie,Set-Cookie,Authorization”,使用Ctrl键加鼠标左键点击,可以查看“sensitive-Headers”配置在代码中的默认值:

/**
* List of sensitive headers that are not passed to downstream requests. Defaults to a
* "safe" set of headers that commonly contain user credentials. It's OK to remove
* those from the list if the downstream service is part of the same system as the
* proxy, so they are sharing authentication data. If using a physical URL outside
* your own domain, then generally it would be a bad idea to leak user credentials.
*/
private Set<String> sensitiveHeaders = new LinkedHashSet<>(
Arrays.asList("Cookie", "Set-Cookie", "Authorization"));

上面有一段注释,我们翻译过来为:

sensitiveHeaders是未传递给下游请求的敏感头列表。默认为一组通常包含用户凭据的“安全”头。如果下游服务与代理是同一系统的一部分,则可以从列表中删除这些服务,以便它们共享身份验证数据。如果在自己的域之外使用物理URL,那么通常泄漏用户凭据是一个坏主意。

上面的翻译通俗一点来说就是,sensitiveHeaders是过滤一些Http请求中Head部分中的敏感字段的,换言之,zuul会根据sensitiveHeaders配置改写request中的头部信息。
sensitiveHeaders默认就过滤掉了"Cookie"、"Set-Cookie"以及"Authorization",即这些参数不会传递各位下游服务。例如客户端在发请求时带了Cookie,那么Cookie不会传递给下游服务。
如果我们需要共享身份验证数据,则不能过滤上面的几个参数,此时我们需要设置“zuul.sensitiveHeaders=”,即不过滤任何信息,如果我们不配置的话,默认就会过滤"Cookie"、"Set-Cookie"以及"Authorization"。
当我们在安全域外使用URL进行访问,最好还是需要过滤这些敏感Head。

PS:
对HTTP请求协议的Head头部信息中"Cookie"、"Set-Cookie"以及"Authorization"参数的意义不太了解的小伙伴,我们来解释一下:
(1)Cookie
解释:Cookie是保存在用户浏览器端的,并在发出http请求时会默认携带的一段文本片段。它可以用来做用户认证,服务器校验等通过文本数据可以处理的问题。
用途:记录用户的登录状态(web服务器会在用户登录成功后下发一个签名(sessionid)来标记session的有效性,这样免去了用户多次认证和登录网站)、记录用户的访问状态(导航、用户的注册流程)、存储用户个性化信息等。
(2)Set-Cookie
Set-Cookie由服务器发送,它包含在响应请求的头部中,主要用于在客户端创建一个Cookie。
默认情况下,服务器在响应消息头部设置set-Cookie来指示浏览器更新sessionid。
(3)Authorization
Authorization一般用来做访问控制,当服务端使用了HTTP基本认证(Basic Authentication)时,就需要在请求头中放置Authorization参数发送给客户端(一般Authorization参数为"用户名+冒号+密码"用BASE64算法加密后的字符串),服务器认证通过后,方向,如果不通过或请求中无Authorization,服务器会返回一个401 Unauthozied给客户端。
 
2、Ignored Headers
刚刚在上面的sensitive-headers知识为user的微服务路由设置了了Head过滤,如果Zuul的每一个路由都要设置Head的敏感过滤,需要通过“zuul.ignoredHeaders”参数来实现,具体的配置格式为(xml):

zuul.ignoredHeaders=[要过滤的敏感头]

在yml配置文件中:

zuul:
ignore-security-headers: [要过滤的敏感头]

这样Zuul就会为所有路由拦截上述敏感头部信息。

默认情况下,ignoredHeaders的参数是空的,但是这里要注意的是,如果我们使用了权限安全框架Spring Security,则ignoredHeaders的参数会默认设置为:

Pragma,Cache-Control,X-Frame-Options,X-Content-Type-Options,XSS-Protection,Expires

如果我们不需要拦截上述Head,我们可以使用zuul.ignoreSecurityHeaders参数,将其设置为false即可(默认是true)。

PS:

以上参数的解释:
(1)Pragma
Pragma只用于客户端发送的请求中,客户端会要求所有的中间服务器不返回缓存的资源。
pragma是http/1.1之前版本的历史遗留字段,仅作为与http的向后兼容而定义。

(2)Cache-Control
Cache-Control的作用和Pragma类似,可以控制缓存的生存时间,其中“”,而“max-age”表示客户端将这个缓存最多保存多少秒。
在不返回缓存的情况下,如果所有的中间服务器都以实现http/1.1为标准,那么直接使用Cache-Control:no-cache即可,如果不是的话,就要包含两个字段:

Cache-Control:no-cache
Pragme:no-cache

(3)X-Frame-Options
在服务端HTTP头部中设置X-Frame-Options,为的是防止网页被别人的网站iframe。
X-Frame-Options 响应头有三个可选的值:
DENY:页面不能被嵌入到任何iframe或frame中;
SAMEORIGIN:页面只能被本站页面嵌入到iframe或者frame中;
ALLOW-FROM:页面允许frame或frame加载。

(4)X-Content-Type-Options
X-Content-Type-Options 响应首部相当于一个提示标志,被服务器用来提示客户端一定要遵循在 Content-Type首部中对MIME 类型的设定,而不能对其进行修改。这就禁用了客户端的MIME类型嗅探行为。
语法:

X-Content-Type-Options: nosniff

● 请求类型是"style" 但是 MIME 类型不是 "text/css",
● 请求类型是"script" 但是 MIME 类型不是  JavaScript MIME 类型。

(5)XSS-Protection
使用XSS-Protection参数,当检测到跨站脚本攻击 (XSS)时,浏览器将停止加载页面。
语法:

X-XSS-Protection: 0
X-XSS-Protection: 1
X-XSS-Protection: 1; mode=block
X-XSS-Protection: 1; report=<reporting-uri>

解释:
0:禁止XSS过滤。
1:启用XSS过滤(通常浏览器是默认的)。如果检测到跨站脚本攻击,浏览器将清除页面(删除不安全的部分)。
1;mode=block:启用XSS过滤。 如果检测到攻击,浏览器将不会清除页面,而是阻止页面加载。
1; report=<reporting-URI> :启用XSS过滤。 如果检测到跨站脚本攻击,浏览器将清除页面并使用CSP report-uri指令的功能发送违规报告。

(6)Expires
Expires指的是过期时间,是HTTP控制缓存的基本手段,这个属性告诉缓存器:相关副本在多长时间内是新鲜的。过了这个时间,缓存器就会向源服务器发送请求,检查文档是否被修改。
例子:

Expires: Wed, 21 Oct 2015 07:28:00 GMT

其效果和Cache-Control类似,也是可以控制缓存的生存时间,两者的不同为,Cache-Control是设置生存时间长度,而Expires是设置截止的具体时间点,即在此时候之后,响应就算过期。

三、Zuul有关路由的一些管理服务

如果我们在Spring Boot工程中使用了@EnableZuulProxy注解开启Zuul,则我们就可以有两个服务节点可以使用:
● Routes
● Filters

1、Routes管理节点
Zuul使用actuator(Spring Boot中用于健康检查、审计、统计和监控的组件)提供了一个“/routes”的Get服务,用来展示我们所有的路由映射列表,而通过“ /routes/details”服务,可以查看配置映射的详情信息。

修改一下之前的Zuul工程,使用“management.endpoints.web.exposure.include”将所有的服务端点服务全部暴露出来:

management:
endpoints:
web:
exposure:
include:
"*"

同之前一样,我们启动Zuul工程、Eureka工程以及User工程,首先我们访问一下“http://localhost:8040/actuator”服务,看一下现在能提供给我们的监控服务有哪些:

【Spring Cloud总结】26.Zuul的各种姿势_/routes


可以看到,“/routes”和“filters”服务都在其中。下面访问一下“http://localhost:8040/actuator/routes”,可以看到我们配置的所有Zuul路由映射:

【Spring Cloud总结】26.Zuul的各种姿势_sensitiveHeaders_02


这里我们只配置了user的,所以只能看到这个。

然后访问一下“http://localhost:8040/actuator/routes/details”,可以看到路由映射的详情:

【Spring Cloud总结】26.Zuul的各种姿势_Zuul_03

2、/filters管理节点

通过“/filters”服务,我们可以查看目前Zuul配置的所有过滤器。

我们访问一下“http://localhost:8040/actuator/filters”,可以看到:

【Spring Cloud总结】26.Zuul的各种姿势_Zuul_04


可以看到,Zuul默认配置了诸如“SendResponseFilter”(发送响应过滤器)、“RibbonRoutingFilter”(Ribbon路由过滤器)等过滤器。

四、Strangulation模式和本地转发

1、Strangulation模式
Strangulation模式被称为“绞杀者模式”,其意思就是“扼杀”旧的端点,慢慢地用不同的新端点服务来替换它们。该模式多用于单体架构的服务改造成微服务,此时我们可以使用“绞杀者模式”慢慢将其转化为微服务架构。这里我们不进行详细的探讨,感兴趣的同学可以看一下Martin Fowler写的有关该模式的英文文章原文:
​​​http://www.martinfowler.com/bliki/StranglerApplication.html​

2、本地转发
一般Zuul设置的都是下游服务的路由转发,当然我们也可以设置本地的转发,在路由参数的url中,前缀加上“forward:”,然后加上本地的真实路径,就可以实现本地服务转发,例如以下配置(官方示例):

zuul:
routes:
first:
path: /first/**
url: https://first.example.com
second:
path: /second/**
url: forward:/second
third:
path: /third/**
url: forward:/3rd
legacy:
path: /**
url: https://legacy.example.com

这里的“/second/**”和“/third/**”的所有请求,就会被转发到本地的“/second”和“/3rd”服务上。

参考:《51CTO学院Spring Cloud高级视频》
​​​​http://www.360doc.com/content/17/0825/13/31406094_682023078.shtml​