看看大牛是怎么理解微服务网关与用户身份识别,创建Zuul网关服务
Zuul的基础使用
Zuul是Netflix公司的开源网关产品,可以和Eureka、Ribbon、Hystrix等组件配合使用。Zuul的规则引擎和过滤器基本上可以用任何JVM语言编写,内置支持Java和Groovy。
在Spring Cloud框架中,Zuul的角色是网关,负责接收所有的REST请求(如网页端、App端等),然后进行内部转发,是微服务提供者集群的流量入口。本书将Zuul称为内部网关,以便和Nginx外部网关相区分。
Zuul的功能大致有:
(1)路由:将不同REST请求转发至不同的微服务提供者,其作用类似于Nginx的反向代理。同时,也起到了统一端口的作用,将很多微服务提供者的不同端口统一到了Zuul的服务端口。
(2)认证:网关直接暴露在公网上时,终端要调用某个服务,通常会把登录后的token(令牌)传过来,网关层对token进行有效性验证。如果token无效(或没有token),就不允许访问REST服务。可以结合Spring Security中的认证机制完成Zuul网关的安全认证。
(3)限流:高并发场景下瞬时流量不可预估,为了保证服务对外的稳定性,限流成为每个应用必备的一道安全防火墙。如果没有这道安全防火墙,那么请求的流量超过服务的负载能力时很容易造成整个服务的瘫痪。
(4)负载均衡:在多个微服务提供者之间按照多种策略实现负载均衡。
创建Zuul网关服务
Spring Cloud对Zuul进行了整合与增强。Zuul作为网关层,自身也是一个微服务,跟其他服务提供者一样都注册在Eureka Server上,可以相互发现。Zuul能感知到哪些Provider实例在线,同时通过配置路由规则可以将REST请求自动转发到指定的后端微服务提供者。
新建Zuul网关服务项目时需要在启动类中添加注解@EnableZuulProxy,声明这是一个网关服务提供者。当然,也需要在pom.xml文件中手动添加如下依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
启动类的代码如下:
package com.crazymaker.springcloud.cloud.center.zuul;
...
@EnableAutoConfiguration(exclude = {SecurityAutoConfiguration.class})
@SpringBootApplication(scanBasePackages =
{"com.crazymaker.springcloud.cloud.center.zuul",
"com.crazymaker.springcloud.standard",
"com.crazymaker.springcloud.user.info.contract"
})
@EnableScheduling
@EnableHystrix
@EnableDiscoveryClient
//开启网关服务
@EnableZuulProxy
@EnableCircuitBreaker
public class ZuulServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulServerApplication.class, args);
}
}
Zuul路由规则配置
作为反向代理,Zuul需要通过路由规则将REST请求转发到上游的微服务Provider。作为示例,下面列出crazy-springcloud脚手架中Zuul网关的路由规则配置:
#服务网关配置
zuul:
ribbonIsolationStrategy: THREAD
host:
connect-timeout-millis: 600000
socket-timeout-millis: 600000
#路由规则
routes:
seckill-provider:
path: /seckill-provider/**
serviceId: seckill-provider
strip-prefix: false
message-provider:
path: /message-provider/**
serviceId: message-provider
strip-prefix: false
user-provider:
path: /user-provider/**
serviceId: user-provider
strip-prefix: false
backend-provider:
path: /backend-provider/**
serviceId: backend-provider
strip-prefix: false
generate-provider:
path: /generate-provider/**
serviceId: generate-provider
strip-prefix: false
sensitiveHeaders: Cookie,Set-Cookie,token,backend,Authorization
demo-provider:
path: /demo-provider/**
serviceId: demo-provider
strip-prefix: false
urlDemo:
path: /blog/**
url: https://www.cnblogs.com
sensitiveHeaders: Cookie,Set-Cookie,token,backend,Authorization
以上示例中,有两种方式的路由规则配置:
(1)路由到直接URL;
(2)路由到微服务提供者。
先看第一种方式的路由规则配置:路由到直接URL。在上述示例中,有一条名为urlDemo的路由规则,该规则匹配到格式为/blog/**的所有URL请求,直接转发到https://www.cnblogs.com的地址上。
比如,通过网关访问如下URL:
http://127.0.0.1:7799/blog/crazymakercircle/p/9904544.html
此URL满足/blog/**的匹配规则,将被Zuul直接转发到上游的URL地址:
https://www.cnblogs.com/crazymakercircle/p/9904544.html
修改地址为疯狂创客圈的社群博客的实际地址,在浏览器中看到的Zuul转发结果如图6-2所示。
图6-2 Zuul直接转发到上游的URL地址
再看第二种方式的路由规则配置:路由到微服务提供者。
比如在上述代码中,有一条名为user-provider的路由规则,该规则将匹配/user-provider/**的所有URL请求,直接路由到名为user-provider的某个微服务提供者。
两种方式的区别如下:
(1)第一种方式使用url属性来指定直接的上游URL的前缀;第二种方式使用serviceId属性来指定上游服务提供者的名称。
(2)第二种方式需要结合Eureka Client客户端来实现动态的路由转发功能,启动类需要加上注解@EnableDiscoveryClient,只能用于Spring Cloud架构中。其实该注解也可以不加,因为网关注解@EnableZuulProxy已经默认进行了导入。
使用第二种方式,配置文件中增加Eureka Client客户端的相关配置如下:
eureka:
client:
serviceUrl:
defaultZone: http://${EUREKA_ZONE_HOST:localhost}:7777/eureka/
instance:
prefer-ip-address: true #访问路径可以显示IP地址
instance-id: ${spring.cloud.client.ip-address}:${server.port}
ip-address: ${spring.cloud.client.ip-address}
过滤敏感请求头部
在同一个系统中,在不同Provider之间共享请求头是可行的,但是,如果Zuul需要将请求转发到外部,可能不希望敏感的请求头泄露到外部的其他服务器。
防止请求头泄露的方式之一是,在Zuul的路由配置中指定要忽略的请求头列表,并且多个敏感头部之间可以用逗号隔开。下面是一个简单的实例:
spring:
application:
name: cloud-zuul
zuul:
sensitiveHeaders: Cookie,Set-Cookie,token,backend,Authorization
大家知道,Cookie经常用于在流量中缓存用户的会话、用户凭证等信息,对于外部系统而言是需要保密的,所以应该设置为敏感标题,不应该带往系统外部。
默认情况下,Zuul转发请求时会把header清空,如果在微服务集群内部转发请求,上游Provider就会收不到任何头部。如果需要传递原始的header信息到最终的上游,就需要添加如下敏感头部设置:
zuul.sensitive-headers=
上面配置了敏感头部为空,YML格式的配置也需要进行空配置,表示没有需要屏蔽的头部。上面是全局配置,也可对单个路由规则进行局部配置,格式如下:
zuul.routes.xxxapi-xxx.sensitiveHeaders=比如crazy-springcloud脚手架中专门对外部的转发规则urlDemo进行了请求头的屏蔽,它的配置如下:
#服务网关路由规则
zuul:
routes:
urlDemo:
path: /blog/**
url: https://www.cnblogs.com
sensitiveHeaders: Cookie,Set-Cookie,token,backend,Authorization
对于该规则自身而言,单个路由规则的局部配置会覆盖全局的设置。
路径前缀的处理
如果不进行任何配置,默认情况下Zuul会去掉路由的路径前缀。
例如,从客户端发起一个请求:
http://crazydemo.com:7799/demo-provider/api/demo/hello/v1
在Zuul进行路由处理时,会去掉在路由规则清单中配置的路径前缀demo-provider。处理之后,转发到上游的服务提供者的URL将变成下面的样子:
http://{provider-ip}:{provider-port}/api/demo/hello/v1
如果上游的微服务提供者没有配置路径前缀,Zuul的这种默认处理和转发就不会有问题。但是,如果上游提供者配置了统一的路径前缀,而前缀被去掉,上游服务提供者就会报出404的错误,也就是找不到URL对应的资源。
比如,在crazy-springcloud脚手架中的所有服务提供者都是配有context-path路径前缀的,如此配置的优势之一是会使下游Nginx外部网关进行代理转发时更加灵活。
从微服务demo-provider的配置文件
src/main/resources/bootstrap.yml可以看出,它的context-path路径前缀为/demo-provider,具体配置内容如下:
server:
port: 7700
servlet:
context-path: /demo-provider
在Zuul进行路由处理时,如何保留请求URL中的路径前缀呢?具体来说,可以设置配置项stripPrefix的值为false,确保路径前缀不会截取掉。stripPrefix的值默认为true。
demo-provider的路由规则具体如下:
#服务网关路由规则
zuul:
routes:
demo-provider:
path: /demo-provider/**
serviceId: demo-provider
strip-prefix: false