SpringCloud:Gateway组件
一、什么是网关
Spring Cloud Gateway是Spring官方基于Spring 5.0,Spring Boot 2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。Spring Cloud Gateway作为Spring Cloud生态系中的网关,目标是替代ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:身份认证和权限校验、服务路由和负载均衡、请求限流等功能。
所有想要访问接口的客户端、用户等都先将请求发至网关,再由网关来解决将该请求交给哪个服务进行处理。
二、工作原理
客户端向Spring Cloud Gateway发出请求。如果网关处理程序映射确定请求与路由匹配,则将其发送到网关Web处理程序。该处理程序通过特定于请求的过滤器链运行请求。筛选器由虚线分隔的原因是,筛选器可以在发送代理请求之前和之后运行逻辑。所有“前置”过滤器逻辑均被执行。然后发出代理请求。发出代理请求后,将运行“后”过滤器逻辑。
简而言之:客户端发起请求->由Predicate里面所定义的规则选去合适的路由->网关处理程序执行Filter(前置)->转发请求并执行过滤器(后置)
三、gateway的三大概念
- Route 路由:gateway的基本构建模块。它由ID、目标URI、断言集合和过滤器集合组成。如果聚合断言结果为真,则匹配到该路由。
- Predicate 断言:Java8中的断言函数。Spring Cloud Gateway中的断言函数输入类型是Spring5.0框架中的ServerWebExchange。Spring Cloud Gateway中的断言函数允许开发者去定义匹配来自于http request中的任何信息,比如请求头和参数等
- Filter 过滤器:一个标准的Spring webFilter。Spring cloud gateway中的filter分为两种类型的Filter,分别是Gateway Filter和Global Filter。过滤器Filter将会对请求和响应进行修改处理
四、案例
client:feign接口
demo:消费者
gateway-gateway:网关
gateway-service:提供者
<!--引入gateway 网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
Goods
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Goods {
private int id;
private String title;
//商品标题
private double price;
//商品价格
private int count;
//商品库存
}
client
@FeignClient(value = "gateway-service")
public interface GoodsFeignClient {
@GetMapping("/gateway-service/goods/findOne/{id}")
Goods findOne(@PathVariable("id") int id);
}
demo
@RestController
@RequestMapping("/order")
public class OrderController {
@Resource
private GoodsFeignClient goodsFeignClient;
@GetMapping("/goods/{id}")
public Goods findOne(@PathVariable("id") int id){
return goodsFeignClient.findOne(id);
}
}
spring:
application:
name: demo
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
server:
port: 8003
servlet:
context-path: /${spring.application.name}
service
@RestController
@RequestMapping("/goods")
public class GoodsController {
@GetMapping("/findOne/{id}")
public Goods findOne(@PathVariable("id") int id){
Goods goods = new Goods(id, "华为手机", 3999, 10000);
return goods;
}
}
spring:
application:
name: gateway-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
server:
port: 8002
servlet:
context-path: /${spring.application.name}
gateway
spring:
application:
name: gateway-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
# 网关配置
gateway:
# 路由配置:转发规则
routes: #集合。
# id: 唯一标识。默认是一个UUID
# uri: 转发路径
# predicates: 条件,用于请求网关路径的匹配规则
- id: demo
# 静态路由
#uri: http://localhost:8003
# 动态路由
uri: lb://demo
predicates:
- Path=/demo/order/**
# - Path=/**
- id: gateway-service
uri: lb://gateway-service
predicates:
- Path=/gateway-service/goods/**
discovery:
locator:
enabled: true #开启动态路由 从微服务中获取服务名
lower-case-service-id: true #允许小写
server:
port: 8000
servlet:
context-path: /${spring.application.name}
网关的路由配置总共需要配置三项
1、id是该路由的唯一标识,
2、uri指定的是需要路由到的服务地址,
3、predicates表示断言,如果满足断言的要求,则网关便会将请求交给uri指定的服务.
静态路由和动态路由区别
静态路由:将uri 写死
动态路由:lb://服务名称
lb:// 表示将请求负载均衡到 demo 服务。断言的形式有很多,比如这里使用的Path,它是用来判断请求路径的,当请求路径以/demo/order开头,该请求就会被这一网关配置处理。
注意:
使用path 去判断路径会出现找不到服务,这时候需要去看看是否有添加项目路径在配置文件中。此处多尝试一下。
测试
请求路径(get)
localhost:8000/demo/order/goods/1
返回参数
{
"id": 1,
"title": "华为手机",
"price": 3999.0,
"count": 10000
}
五、路由配置
在刚刚的例子中,我们使用到了一个路径的路由断言,只需要在-Path中配置/demo/order/**,那么以/demo/order/开头的请求就会被网关处理,这是如何实现的呢?事实上,Gateway中有很多的路由断言工厂,当我们在配置文件中对断言进行配置后,这些配置就会被路由断言工厂进行解析并处理,而-Path配置就是由org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory来处理的。SpringCloud Gateway中一共提供了11种基本的路由断言工厂,分别如下:
1.BeforeRoutePredicateFactory:判断是否为某个时间点之前的请求
2.AfterRoutePredicateFactory:判断是否为某个时间点之后的请求
3.BetweenRoutePredicateFactory:判断是否为某两个时间点之间的请求
4.CookieRoutePredicateFactory:判断是否包含某些cookie
5.HeaderRoutePredicateFactory:判断是否包含某些header
6.HostRoutePredicateFactory:判断请求是否是访问某个host
7.MethodRoutePredicateFactory:判断请求方式是否是指定的方式
8.PathRoutePredicateFactory:判断请求路径是否满足规则
9.QueryRoutePredicateFactory:判断请求参数是否包含指定的参数
10.RemoteAddrRoutePredicateFactory:判断请求ip是否在指定的范围内
11.WeightRoutePredicateFactory:权重处理
5.1、通过After匹配
After Route Predicate Factory采用一个参数——日期时间。在该日期时间之后发生的请求都将被匹配。
spring:
cloud:
gateway:
routes:
- id: time_route
uri: http://time.demo
predicates:
- After=2021-11-03T06:06:06+08:00[Asia/Shanghai]
5.2、通过Before匹配
Before Route Predicate Factory采用一个参数——日期时间。在该日期时间之前发生的请求都将被匹配。
spring:
cloud:
gateway:
routes:
- id: time_route
uri: http://time.demo
predicates:
- Before=2021-11-03T06:06:06+08:00[Asia/Shanghai]
5.3、通过Between匹配
Between 路由断言 Factory有两个参数,datetime1和datetime2。在datetime1和datetime2之间的请求将被匹配。datetime2参数的实际时间必须在datetime1之后。
spring:
cloud:
gateway:
routes:
- id: between_route
uri: http://example.org
predicates:
- Between=2021-11-01T06:06:06+08:00[Asia/Shanghai], 2021-11-06T06:06:06+08:00[Asia/Shanghai]
5.4、通过Cookie匹配
Cookie 路由断言 Factory有两个参数,cookie名称和正则表达式。请求包含次cookie名称且正则表达式为真的将会被匹配。
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://example.org
predicates:
- Cookie=chocolate, ch.p
5.5、通过Header匹配
Header 路由断言 Factory有两个参数,header名称和正则表达式。请求包含次header名称且正则表达式为真的将会被匹配。
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://example.org
predicates:
- Header=X-Request-Id, \d+
5.6、通过Host匹配
Host 路由断言 Factory包括一个参数:host name列表。使用Ant路径匹配规则,
.作为分隔符。
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://example.org
predicates:
- Host=**.somehost.org,**.anotherhost.org
5.7、通过Method匹配
Method 路由断言 Factory只包含一个参数: 需要匹配的HTTP请求方式
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://example.org
predicates:
- Method=GET #所有get请求都被路由
5.8、通过Path匹配
Path 路由断言 Factory 有2个参数: 一个Spring
PathMatcher表达式列表和可选
matchOptionalTrailingSeparator标识 .
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://example.org
predicates:
- Path=/foo/{segment} #请求路径符合要求,则此路由将匹配,例如:/foo/1 或者 /foo/bar
5.9、通过Query匹配
Query 路由断言 Factory 有2个参数: 必选项
param和可选项
regexp.
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://example.org
predicates:
- Query=baz #则包含了请求参数baz的都将被匹配。
- Query=foo, ba. # 如果请求参数里包含foo参数,并且值匹配为ba.表达式,则将会被路由,如:bar and baz
5.10、通过RemoteAddr匹配
RemoteAddr 路由断言 Factory的参数为 一个CIDR符号(IPv4或IPv6)字符串的列表,最小值为1,例如192.168.0.1/16(其中192.168.0.1是IP地址并且16是子网掩码)。
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://example.org
predicates:
- RemoteAddr=192.168.1.1/24 #如果请求的remote address 为 192.168.1.10则将被路由
多个Predicates同时存在于同一个路由时,请求必须同时满足所有的条件才会被这个路由匹配。
六、Gateway过滤器
1、概述
在路由的配置中,我们还可以配置一项filter,它是Gateway提供的过滤器,可以对进入网关的请求和返回的响应进行相应的处理。与路由断言类似,Gateway同样提供了过滤器工厂。官网上又两大类过滤器,加起来三十多种。
2、局部过滤器
- 比如第一个AddRequestHeader GatewayFilterFactory,从名字就能够看出来,这是用来添加请求头信息的,具体配置方式如下:
spring:
cloud:
gateway:
routes:
- id: demo
uri: lb://demo
predicates:
- Path=/demo/order/**
filters:
- AddRequestParameter=username,zhangsan #修改接口发现测试成功
3、全局过滤器
- 为了更加灵活地适应各种场景,Gateway还提供了一种特殊的过滤器—GlobalFiler(全局过滤器),它的作用与default-filter类似,区别在于GlobalFilter需要我们自己去实现,要做的就是实现GlobalFilter接口。
@Order(0) //数值越小 越先执行
@Component
public class MyFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("自定义全局过滤器执行了~~~");
return chain.filter(exchange);//放行
}
}
案例
@Order(0)
@Component
public class AuthenticationFilter implements GlobalFilter, Ordered {
@Autowired
private RedisTemplate redisTemplate ;
/**
*
* @param exchange 网关与web环境交换机
* @param chain 过滤器链
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String method = request.getMethodValue();
String path = request.getURI().getPath();
//登陆请求直接放行
if(method.equalsIgnoreCase("post") && path.endsWith("/user")){
return chain.filter(exchange);
}
//获取请求中携带的token数据
List<String> headers = request.getHeaders().get("token");
//用户未携带token , 直接返回错误信息给客户端
if(headers==null || headers.size()<=0){
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
//获取客户端发送的token
String token = headers.get(0);
//判断token是否存在 , 如果不存在说明未登录,或者登陆状态过期
Boolean flag = redisTemplate.hasKey(token);
if(!flag){
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
//放行 , 访问资源
return chain.filter(exchange);
}
七、处理跨域
1、概念
跨域问题是指浏览器禁止请求的发起者与服务端发生跨域的ajax请求。
跨域是每一个前后端分离项目都需要面临的问题,但有了网关,我们就可以将处理跨域的流程写在网关里,无需在每一个微服务中都进行配置了。
2、配置
浏览器在向服务器发起请求之前,会先发送一个option请求进行询问,查看是否满足要求,为了防止这个询问请求被拦截,所以需要配置 `add-to-simple-url-handler-mapping: true` ; `[/**]` 表示对所有的请求进行处理; `maxAge: 360000` 用于配置跨域检测的有效时间,当浏览器发送了一次option请求进行询问并且成功后,在这段有效时间内,服务器将不再要求对浏览器发送过来的请求进行检测,由此提高了性能。
spring:
cloud:
gateway:
globalcors: # 全局跨域处理
add-to-simple-url-handler-mapping: true # 解决options询问请求被拦截的问题
cors-configurations:
'[/**]':
allowedOrigins: # 配置允许哪些网站跨域 通常用 "*" 表示
- "http://localhost:8000"
- "http://localhost:9000"
allowedMethods: # 配置允许哪些请求方式跨域
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 允许请求携带Cookie
maxAge: 360000 # 跨域检测的有效时间
解决options询问请求被拦截的问题
cors-configurations:
'[/**]':
allowedOrigins: # 配置允许哪些网站跨域 通常用 "*" 表示
- "http://localhost:8000"
- "http://localhost:9000"
allowedMethods: # 配置允许哪些请求方式跨域
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 允许请求携带Cookie
maxAge: 360000 # 跨域检测的有效时间