由于我们在之前所有的入门教程中,对于HTTP请求都采用了简单的接口实现。而实际使用过程中,我们的HTTP请求要复杂的多,比如当我们将Spring Cloud Zuul作为API网关接入网站类应用时,往往都会碰到下面这两个非常常见的问题:
本文将帮助大家分析问题原因并给出解决这两个常见问题的方法。
一、会话保持问题
通过跟踪一个HTTP请求经过Zuul到具体服务,再到返回结果的全过程。我们很容易就能发现,在传递的过程中,HTTP请求头信息中的Cookie和Authorization都没有被正确地传递给具体服务,所以最终导致会话状态没有得到保持的现象。
那么这些信息是在哪里丢失的呢?我们从Zuul进行路由转发的过滤器作为起点,来一探究竟。下面是RibbonRoutingFilter过滤器的实现片段:
- public class RibbonRoutingFilter extends ZuulFilter{
- ...
- protected ProxyRequestHelper helper;
-
- @Override
- 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);
- ...
- }
- }
这里有三个重要元素:
- 过滤器的核心逻辑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
- @ConfigurationProperties("zuul")
- public class ZuulProperties{
- private Set<String> sensitiveHeaders = new LinkedHashSet<>(
- Arrays.asList("Cookie", "Set-Cookie", "Authorization"));
- ...
- }
所以解决该问题的思路也很简单,我们只需要通过设置sensitiveHeaders即可,设置方法分为两种:
1. 全局设置:
2. 指定路由设置:
- zuul.routes..sensitive-headers=
- zuul.routes..custom-sensitive-headers=true
二、重定向问题
在使用Spring Cloud Zuul对接Web网站的时候,处理完了会话控制问题之后。往往我们还会碰到如下图所示的问题,我们在浏览器中通过Zuul发起了登录请求,该请求会被路由到某WebSite服务,该服务在完成了登录处理之后,会进行重定向到某个主页或欢迎页面。此时,仔细的开发者会发现,在登录完成之后,我们浏览器中URL的HOST部分发生的改变,该地址变成了具体WebSite服务的地址了。这就是在这一节,我们将分析和解决的重定向问题!
出现该问题的根源是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过滤器的源码来详细更多实现细节。
默认过滤的header
spring-cloud-netflix-core-1.2.6.RELEASE-sources.jar!/org/springframework/cloud/netflix/zuul/filters/ZuulProperties.java
/**
* List of sensitive headers that are not passed to downstream requests. Defaults to a
* "safe" set of headers that commonly contain user credentials. It
* 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"));
配置
zuul:
sensitiveHeaders:
host:
socket-timeout-millis: 60000
connect-timeout-millis: 60000
通过显示指定为空,表示让zuul的过滤header列表为空,这样就可以正常返回了
spring读取cookie方法
String xxx = WebUtils.getCookie((HttpServletRequest) servletRequest,"your-cookie-name").getValue();
http://zhuanlan.51cto.com/art/201705/538644.htm
相关推荐
springcloud入门代码基于Spring Cloud实现的服务网关Zuul源代码 安装教程 Zuul pom.xml <groupId>org.springframework.cloud <artifactId>spring-cloud-starter-netflix-zuul application.yml server:...
Spring Cloud Zuul2 Zuul与Spring Cloud的集成。 警告:正在进行中入门启动zuul服务器./src/test/java/rocks/devmesh/spring/cloud/zuul/Application#main请求到'/ healthcheck'端点$ curl ...
主要介绍了SpringCloud实战小贴士之Zuul的路径匹配,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
spring cloud 客户端和spring cloud zuul ,和前面的两个是一个系列,先打开cloud service 的服务,然后打开 product ,在打开客户端,最后可以打开zuul 可以实现eurake的负载均衡,zuul的负载均衡。
spring-cloud-netflix-zuul-websocket, Zuul反向代理网络套接字支持 spring-cloud-netflix-zuul-websocketspring 应用程序中支持Zuul反向代理web套接字支持的简单库。用法spring-cloud-netflix-zuul-websocket可以从...
SpringCloud之六 路由网关zuul,使用Zuul来进行网关路由,以及安全验证的处理
Zuul、Ribbon、Feign、Turbine、Hystrix、Oauthor2、Sleuth、API Gateway、Spring Cloud、Config Eureka、SSO、Spring Cloud、 BUS、Turbine、Zipkin、Cache、Spring Cloud Admin、API Gateway、ELK Spring Cloud ...
SpringCloud zuul 组件的jar包,可以用作联调SpringCloud测试使用。
spring-cloud-zuul:Spring Cloud Zuul使用高级篇 Filter 鉴权 熔断 重试 spring-cloud-sleuth-zipkin: 利用Sleuth、Zipkin对Spring Cloud应用进行服务追踪分析 spring-boot-admin-eureka: 使用Spring Boot Admin ...
# Spring Cloud Examples Spring Cloud 使用的各种示例,以最简单、最实用为标准 [Spring Cloud 中文索引](https://github.com/ityouknow/awesome-spring-cloud) | [Spring Boot学习示例代码]...
【微服务架构】SpringCloud之路由网关(zuul)https://blog.csdn.net/u012081441/article/details/80771468
23-Spring Cloud网关&负载均衡 Zuul&Ribbon1
Spring Cloud(zuul)使用例子,通过代码学习Spring Cloud(zuul)使用。
Zuul包含了对请求的路由和过滤两个最主要的功能: 其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等...
spring-cloud-zuul:Spring Cloud Zuul使用高级篇 Filter 鉴权 熔断 重试 spring-cloud-sleuth-zipkin: 利用Sleuth、Zipkin对Spring Cloud应用进行服务追踪分析 spring-boot-admin-eureka: 使用Spring Boot Admin ...
SpringCloud实战学习,本项目是SpringCloud实战学习,集成了eureka、fegin、zuul
spring cloud eureka zuul ribbon hystrix feign config 示例代码
Spring Cloud Sleuth超详细实战 源码篇: 深入理解Feign之源码解析 深入理解Eureka之源码解析 深入理解Ribbon之源码解析 深入理解Hystrix之文档翻译 深入理解Zuul之源码解析 番外篇: 如何使用MongoDB+Springboot...
Spring Cloud(六)服务网关 zuul 快速入门 spring-cloud-zuul-filter Spring Cloud(七)服务网关 Zuul Filter 使用 spring-cloud-config Spring Cloud(八)高可用的分布式配置中心 Spring Cloud ...
可用的spring-cloud-starter-netflix-zuul-2.0.0.RELEASE包,maven上的spring-cloud-starter-netflix-zuul-2.0.0.RELEASE已经没了。