【笔记】Gateway统一网关

前言

This project provides a library for building an API Gateway on top of Spring WebFlux. Spring Cloud Gateway aims to provide a simple, yet effective way to route to APIs and provide cross cutting concerns to them such as: security, monitoring/metrics, and resiliency.(官网

引入依赖

pom.xml
1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

将网关加入到Eureka注册中心

添加依赖

pom.xml
1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

添加注解

  • 在主启动类添加注解,启动Eureka客户端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class GatewayApplication {

public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}

}

修改配置

src/main/resources/application.yml
1
2
3
4
5
6
7
8
9
server:
port: 8100
spring:
application:
name: gateway
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8000/eureka

实现请求的路由

添加配置

  • 当请求的路径符合路由的断言规则时,将请求转发到路由的目标路径

spring.cloud.gateway.default-filters:默认过滤器,对所有路由都生效(DefaultFilter)
spring.cloud.gateway.routes.id:路由的id
spring.cloud.gateway.routes.uri:路由的目标路径

lb://服务名:根据服务名发送请求
http://ip地址:根据ip地址发送请求
spring.cloud.gateway.routes.predicates:对指定路由的断言
spring.cloud.gateway.routes.filters:对指定路由的过滤器

src/main/resource/application.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
spring:
cloud:
gateway:
routes:
- id: eureka-service-user
uri: lb://eureka-service-user
predicates:
- Path=/users/**
- id: eureka-service-user
uri: http://127.0.0.1:8082
predicates:
- Path=/orders/**
filters:
- AddRequestHeader=key, value
default-filters:
- AddRequestHeader=key, value

路由的断言

spring.cloud.gateway.routes.predicates.After:指定时间之后
spring.cloud.gateway.routes.predicates.Before:指定时间之前
spring.cloud.gateway.routes.predicates.Between:指定时间之间
spring.cloud.gateway.routes.predicates.Cookie:指定符合条件的Cookie
spring.cloud.gateway.routes.predicates.Header:指定符合条件的Header
spring.cloud.gateway.routes.predicates.Host:指定符合条件的请求域名
spring.cloud.gateway.routes.predicates.Method:指定符合条件的请求方式
spring.cloud.gateway.routes.predicates.Path:指定符合条件的请求地址
spring.cloud.gateway.routes.predicates.Query:指定符合条件的请求参数
spring.cloud.gateway.routes.predicates.RemoteAddr:指定符合条件的请求IP范围
spring.cloud.gateway.routes.predicates.Weight:指定符合条件的组和权重

src/main/resource/application.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
spring:
cloud:
gateway:
routes:
- predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
- Cookie=chocolate, ch.p
- Header=X-Request-Id, \d+
- Host=**.somehost.org,**.anotherhost.org
- Method=GET,POST
- Path=/red/{segment},/blue/{segment}
- Query=green
- RemoteAddr=192.168.1.1/24
- Weight=group1, 2

路由的过滤器

spring.cloud.gateway.routes.filters.AddRequestHeader:添加请求头
spring.cloud.gateway.routes.filters.RemoveRequestHeader:移除请求头
spring.cloud.gateway.routes.filters.SetRequestHeader:设置请求头

spring.cloud.gateway.routes.filters.AddRequestParameter:添加请求参数
spring.cloud.gateway.routes.filters.RemoveRequestParameter:移除请求参数

spring.cloud.gateway.routes.filters.AddResponseHeader:添加响应头
spring.cloud.gateway.routes.filters.RemoveResponseHeader:移除响应头
spring.cloud.gateway.routes.filters.SetResponseHeader:设置响应头
spring.cloud.gateway.routes.filters.RewriteResponseHeader:重写响应头
spring.cloud.gateway.routes.filters.RewriteLocationResponseHeader:重写响应头中的Location

spring.cloud.gateway.routes.filters.PrefixPath:请求路径添加前缀
spring.cloud.gateway.routes.filters.StripPrefix:请求路径修剪前缀

spring.cloud.gateway.routes.filters.SetPath:设置请求路径
spring.cloud.gateway.routes.filters.RewritePath:重写请求路径

spring.cloud.gateway.routes.filters.RequestHeaderSize:请求头大小
spring.cloud.gateway.routes.filters.RedirectTo:请求转发
spring.cloud.gateway.routes.filters.SaveSession:保存Session
spring.cloud.gateway.routes.filters.SetStatus:设置状态码

src/main/resource/application.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
spring:
cloud:
gateway:
routes:
- filters:
- AddRequestHeader=key, value
- AddRequestParameter=red, blue
- AddResponseHeader=X-Response-Red, Blue
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
- PrefixPath=/mypath
- RedirectTo=302, https://acme.org
- RemoveRequestHeader=X-Request-Foo
- RemoveResponseHeader=X-Response-Foo
- RemoveRequestParameter=red
- RequestHeaderSize=1000B
- RewritePath=/red/?(?<segment>.*), /$\{segment}
- RewriteLocationResponseHeader=AS_IN_REQUEST, Location, ,
- RewriteResponseHeader=X-Response-Red, , password=[^&]+, password=***
- SaveSession
- SetPath=/{segment}
- SetRequestHeader=X-Request-Red, Blue
- SetResponseHeader=X-Response-Red, Blue
- SetStatus=401
- StripPrefix=2

全局过滤器(GlobalFilter)

通过注解指定优先级

@Order():指定过滤器在过滤器链上的优先级,数字越小,优先级越高,范围是[-214783647,214783647],默认值为最大值,优先级最低

src/main/java/com/filter/AuthorizeFilter.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.filter;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Order(0)
@Component
public class AuthorizeFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1. 获取请求参数
ServerHttpRequest request = exchange.getRequest();
MultiValueMap<String, String> params = request.getQueryParams();
// 2. 判断请求参数是否指定参数
String param = params.getFirst("key");
if ("value".equals(param)) {
// 3. 通过链接下一个过滤器链,实现放行
return chain.filter(exchange);
}
// 4. 通过直接返回响应,实现拦截
// 4.1 根据业务逻辑设置响应码
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
// 4.2 返回响应
return exchange.getResponse().setComplete();
}
}

通过实现接口指定优先级

  • 通过重写getOrder()方法指定过滤器在过滤器链上的优先级,在返回值中指定,数字越小,优先级越高
src/main/java/com/filter/AuthorizeFilter.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.filter;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1. 获取请求参数
ServerHttpRequest request = exchange.getRequest();
MultiValueMap<String, String> params = request.getQueryParams();
// 2. 判断请求参数是否指定参数
String param = params.getFirst("key");
if ("value".equals(param)) {
// 3. 通过链接下一个过滤器链,实现放行
return chain.filter(exchange);
}
// 4. 通过直接返回响应,实现拦截
// 4.1 根据业务逻辑设置响应码
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
// 4.2 返回响应
return exchange.getResponse().setComplete();
}

@Override
public int getOrder() {
return 0;
}
}

过滤器的排序规则

  • 过滤器有三种类型,分别是:全局过滤器、默认过滤器、路由过滤器
  • 在配置文件中定义过滤器时,先配置的过滤器默认权值从1开始依次递增
  • 先比较权值,权值越小,优先级越高
  • 相同权值的过滤器,再比较类型,类型的优先级是:默认过滤器 > 路由过滤器 > 全局过滤器

举例

1
默认过滤器1 > 路由过滤器1 > 全局过滤器1 > 默认过滤器2 > 路由过滤器3

跨域处理

spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping:解决OPTIONS请求被拦截的问题
spring.cloud.gateway.globalcors.cors-configurations.'':指定哪些请求被跨域处理
spring.cloud.gateway.globalcors.cors-configurations.''.allowedOrigins:指定允许跨域请求的网站
spring.cloud.gateway.globalcors.cors-configurations.''.allowedMethods:指定允许跨域请求的请求类型
spring.cloud.gateway.globalcors.cors-configurations.''.allowedHeaders:指定允许跨域请求的请求头携带信息
spring.cloud.gateway.globalcors.cors-configurations.''.allowCredentials:是否允许携带Cookie
spring.cloud.gateway.globalcors.cors-configurations.''.maxAge:本次跨域检测的有效期

src/main/resources/application.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
spring:
cloud:
gateway:
globalcors:
add-to-simple-url-handler-mapping: true
cors-configurations:
'[/**]':
allowedOrigins:
- "http://simple.com"
allowedMethods:
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*"
allowCredentials: true
maxAge: 360000

踩坑

  • 启动失败,报错:Consider defining a bean of type 'org.springframework.http.codec.ServerCodecConfigurer' in your configuration.

原因

  • 与SpringMVC冲突

解决问题

  • 去除spring-boot-starter-web依赖

完成

参考文献

哔哩哔哩——黑马程序员