Gateway官方文档学习
Spring Cloud Gateway夺命连环10问? (opens new window)
# 何为网关?
什么是网关?理解成火车站的检票口,统一 检票
网关优点: 统一进行操作,去处理一些问题
# 1、网关作用
- 路由
- 负载均衡
- 统一鉴权
- 统一处理跨域
- 统一业务处理(缓存)
- 访问控制
- 发布控制
- 流量染色
- 统一接口保护
- 限制请求
- 信息脱敏
- 降级(熔断)
- 限流 学习令牌桶算法,学习露桶算法,学习一下 RedislimitHandler
- 超时时间
- 重试(业务保护)
- 统一日志
- 统一文档
# 2、具体作用
路由(请求转发)
起到转发的作用,比如有接口 A 和接口 B, 网关会记录这些信息,根据用户访问的地址和参数,转发请求到对应的接口(服务器 / 集群)
用户 a 调用接口 A
/a => 接口 A /b => 接口 B
负载均衡
在路由的基础上可以转发到某一个服务器
/c => 服务A / 集群 A(随机转发到其中的某一个机器)
uri 从固定地址改成 b:xx
统一鉴权
判断用户是否有权限进行操作,无论访问什么接口,我都统一去判断权限,不用重复写
统一处理跨域
网关统一处理跨域,不用在每个项目单独处理
统一业务处理
把每个项目中都要做的通用逻辑放到上层(网关),统一处理,比如本项目的次数统计
访问控制
黑白名单,比如限制 DDOS IP -- 将已知的恶意 IP 地址添加到黑名单中,从而防止这些 IP 地址对系统发起 DDoS 攻击。
黑白名单是一种常见的网络安全策略,用于限制或允许 特定的 IP 地址或 IP 地址范围 访问网络资源。限制 DDoS(分布式拒绝服务攻击)IP 是黑名单的一个典型用例。
发布控制
灰度发布,比如上线新接口,先给新接口分配 20%流量,老接口80%,再慢慢调整比例
流量染色
区分用户来源
给请求(流量)添加一些标识,一般是设置请求头中,添加新的请求头 https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-addrequestheader-gatewayfilter-factory
全局染色:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#default-filters
接口保护
限制请求
信息脱敏
降级(熔断)进行兜底
限流
超时时间 超时就中断
重试(业务保护):
统一日志
统一的请求,响应信息记录
统一文档
将下游项目的文档进行聚合,在一个页面统一查看
建议用:https://doc.xiaominfo.com/docs/middleware-sources/aggregation-introduction (opens new window)
网关的分类
- **全局网关(接入层网关)**作用是负载均衡、请求日志等,不和业务逻辑绑定
- **业务网关(微服务网关)**会有一些业务逻辑,作用是将请求转发到不同的业务/项目/接口/服务
参考文章:https://blog.csdn.net/qq_21040559/article/details/122961395 (opens new window)
实现
- Nginx (全局网关),Kong网关(API网关), 编程成本相对较高
- Spring Cloud Gateway(取代了Zuul)性能高 可以用java代码来写逻辑 适于学习
网关技术选型:https://zhuanlan.zhihu.com/p/500587132 (opens new window)
# Spring Cloud Gateway
全部内容基本来自官网
官网:https://spring.io/projects/spring-cloud-gateway (opens new window)
官方文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference//html/ (opens new window)
中文文档:https://springdoc.cn/spring-cloud-gateway/#gateway-starter (opens new window)
新建项目
在 IDEA 中新建项目 勾选 Gateway、Lombok
参考官网 get started 中的实例代码
@SpringBootApplication
public class DemogatewayApplication {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("path_route", r -> r.path("/get")
.uri("http://httpbin.org"))
.route("host_route", r -> r.host("*.myhost.org")
.uri("http://httpbin.org"))
.route("rewrite_route", r -> r.host("*.rewrite.org")
.filters(f -> f.rewritePath("/foo/(?<segment>.*)", "/${segment}"))
.uri("http://httpbin.org"))
.route("hystrix_route", r -> r.host("*.hystrix.org")
.filters(f -> f.hystrix(c -> c.setName("slowcmd")))
.uri("http://httpbin.org"))
.route("hystrix_fallback_route", r -> r.host("*.hystrixfallback.org")
.filters(f -> f.hystrix(c -> c.setName("slowcmd").setFallbackUri("forward:/hystrixfallback")))
.uri("http://httpbin.org"))
.route("limit_route", r -> r
.host("*.limited.org").and().path("/anything/**")
.filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter())))
.uri("http://httpbin.org"))
.build();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
编写代码:
@SpringBootApplication
public class XuanapiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(XuanapiGatewayApplication.class, args);
}
// 构建自定义路由
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("to_baidu", r -> r.path("/baidu") // 定义路由名称为 "to_baidu"
.uri("http://www.baidu.com/")) // 将匹配的请求转发到 "http://www.baidu.com/"
.build();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
在此配置类中,定义了一个名为 "to_baidu"
的路由规则,当请求的路径是 "/baidu"
时,将被路由到 "http://www.baidu.com/"。
# 1、核心概念
# Glossary (opens new window)
官方文档如下
- Route: The basic building block of the gateway. It is defined by an ID, a destination URI, a collection of predicates, and a collection of filters. A route is matched if the aggregate predicate is true.
- Predicate: This is a Java 8 Function Predicate (opens new window). The input type is a Spring Framework
ServerWebExchange
(opens new window). This lets you match on anything from the HTTP request, such as headers or parameters. - Filter: These are instances of
GatewayFilter
(opens new window) that have been constructed with a specific factory. Here, you can modify requests and responses before or after sending the downstream request.
解析:
路由(根据什么条件,转发到哪里)
谓词 / 断言(一组规则,条件,用来确定如何转发路由)
过滤器:对请求进行一系列的处理,比如添加请求头,添加请求参数
# 请求流程
- 客户端发起请求
- Handler Mapping:根据断言,去将请求转发到对应的路由
- Web Handler:处理请求(一层层经过过滤器)
- 实际调用服务
# 2、两种配置方式
配置式 (方便,规范)能用就用
- 简化版
spring: cloud: gateway: routes: - id: after_route uri: https://example.org predicates: - Cookie=mycookie,mycookievalue
1
2
3
4
5
6
7
8- 全称
spring: cloud: gateway: routes: - id: after_route uri: https://example.org predicates: - name: Cookie args: name: mycookie regexp: mycookievalue
1
2
3
4
5
6
7
8
9
10
11编程式(灵活,相对麻烦) -- 就是自己编写代码定义
# 3、路由的各种断言
目录
- After 在xx时间之后
- Before 在xx时间之前
- Between 在xx时间之间
- 请求类别
- 请求头(包含Cookie)
- 查询参数
- 客户端地址
- 权重
# After
The After Route Predicate Factory
当前时间在这个时间之后,就会访问当前这个路由
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
2
3
4
5
6
7
8
# Before
The Before Route Predicate Factory
当前时间在这个时间之前,就会访问当前这个路由
spring:
cloud:
gateway:
routes:
- id: before_route
uri: https://example.org
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
2
3
4
5
6
7
8
# Between
The Between Route Predicate Factory
当前时间在这个时间之间,就会访问当前这个路由
spring:
cloud:
gateway:
routes:
- id: between_route
uri: https://example.org
predicates:
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
2
3
4
5
6
7
8
# 请求头(包含Cookie)
The Cookie Route Predicate Factory
如果你的请求头cookie的是chocolate,它的值是ch.p,就会访问当前这个路由
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://example.org
predicates:
- Cookie=chocolate, ch.p
2
3
4
5
6
7
8
The Header Route Predicate Factory
如果你的请求头包含 X-Request-Id
这样一个请求头,并且,它的值符合正则表达式的规则,就会访问当前这个路由
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://example.org
predicates:
- Header=X-Request-Id, \d+
2
3
4
5
6
7
8
# 域名
The Host Route Predicate Factory
如果你的访问的是这个 **.somehost.org,.anotherhost.org
,域名,就会访问当前这个路由
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://example.org
predicates:
- Host=**.somehost.org,**.anotherhost.org
2
3
4
5
6
7
8
# 请求类别
The Method Route Predicate Factory
如果你的请求类别是这个 GET、POST,就会访问当前这个路由
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://example.org
predicates:
- Method=GET,POST
2
3
4
5
6
7
8
# 访问的地址
The Path Route Predicate Factory
如果你的访问的地址是以这些 /red/{segment},/blue/{segment}
路径作为前缀,就会访问当前这个路由({segment}
花括号定义占位符)
spring:
cloud:
gateway:
routes:
- id: path_route
uri: https://example.org
predicates:
- Path=/red/{segment},/blue/{segment}
2
3
4
5
6
7
8
参考:https://springdoc.cn/spring-cloud-gateway/#path (opens new window)
# 查询参数
The Query Route Predicate Factory
根据查询条件,比如 red greet green,就会访问当前这个路由
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=red, gree.
2
3
4
5
6
7
8
# 远程地址
The RemoteAddr Route Predicate Factory
根据远程地址,比如你的用户的 ip地址是 192.168.1.1/24,就会访问当前这个路由
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: https://example.org
predicates:
- RemoteAddr=192.168.1.1/24
2
3
4
5
6
7
8
# 权重
The Weight Route Predicate Factory
根据你设置的权重,给你把同一个访问的地址,重定到不同的服务,轻松实现发布控制
spring:
cloud:
gateway:
routes:
- id: weight_high
uri: https://weighthigh.org
predicates:
- Weight=group1, 8
- id: weight_low
uri: https://weightlow.org
predicates:
- Weight=group1, 2
2
3
4
5
6
7
8
9
10
11
12
# XForwarded 远程地址
The XForwarded Remote Addr Route Predicate Factory
从请求头中如果拿到 XForwarded 这个请求头的地址 192.168.1.1/24 就会访问当前这个路由
spring:
cloud:
gateway:
routes:
- id: xforwarded_remoteaddr_route
uri: https://example.org
predicates:
- XForwardedRemoteAddr=192.168.1.1/24
2
3
4
5
6
7
8
# 4、过滤器
基本功能:对请求头、请求参数、响应头的增删改查 1.添加请求求头 2.添加请求参数 3.添加响应头 4.降级 5.限流 6.重试
# 增加请求头(可以用作流量染色)
The AddRequestHeader
GatewayFilter
Factory
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
filters:
- AddRequestHeader=X-Request-red, blue
2
3
4
5
6
7
8
# 增加请求参数
The AddRequestParameter
GatewayFilter
Factory
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: https://example.org
filters:
- AddRequestParameter=red, blue
2
3
4
5
6
7
8
这将为所有匹配的请求在下游请求的查询字符串中添加 red=blue
。
# 添加响应头
The AddResponseHeader
GatewayFilter
Factory
spring:
cloud:
gateway:
routes:
- id: add_response_header_route
uri: https://example.org
filters:
- AddResponseHeader=X-Response-Red, Blue
2
3
4
5
6
7
8
这将把 X-Response-Red:Blue
header 添加到所有匹配请求的下游响应的 header 中。
# 如果响应头中有重复的,去重
The DedupeResponseHeader
GatewayFilter
Factory
spring:
cloud:
gateway:
routes:
- id: dedupe_response_header_route
uri: https://example.org
filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
2
3
4
5
6
7
8
保留策略:第一,最后,随机
The DedupeResponseHeader
filter also accepts an optional strategy
parameter. The accepted values are RETAIN_FIRST
(default), RETAIN_LAST
, and RETAIN_UNIQUE
.
在 Spring Cloud Gateway 的 DedupeResponseHeader 过滤器中,可以使用 strategy 参数来指定响应头的保留策略。该过滤器的作用是去重响应头,确保在多次请求后只保留一个相同的响应头。
可接受的 strategy 参数值有三种:
RETAIN_FIRST:默认值。表示保留第一个出现的响应头,后续相同的响应头会被忽略,只保留第一个。
RETAIN_LAST:表示保留最后一个出现的响应头,之前的相同响应头会被忽略,只保留最后一个。
RETAIN_UNIQUE:表示保留所有不重复的响应头,不论其出现的顺序。
通过设置不同的保留策略,可以控制响应头的去重方式,以满足具体的需求。默认情况下,使用 RETAIN_FIRST 策略,保留第一个出现的响应头。
# 降级
Spring Cloud CircuitBreaker GatewayFilter Factory
需要引入 spring-cloud-starter-circuitbreaker-reactor-resilience4j
包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>
2
3
4
spring:
cloud:
gateway:
routes:
- id: circuitbreaker_route
uri: lb://backing-service:8088
predicates:
- Path=/consumingServiceEndpoint
filters:
- name: CircuitBreaker
args:
name: myCircuitBreaker
fallbackUri: forward:/inCaseOfFailureUseThis
- RewritePath=/consumingServiceEndpoint, /backingServiceEndpoint
2
3
4
5
6
7
8
9
10
11
12
13
14
这个路由规则定义了一个对 "/consumingServiceEndpoint" 路径的请求转发到名为 "backing-service" 的服务的 8088 端口。同时,还使用了熔断器来保护后端服务,当后端服务发生故障时,会使用 "/inCaseOfFailureUseThis" 作为回退 URI,以保证系统的稳定性和可用性。
降级处理器,写一下降级规则
The FallbackHeaders
GatewayFilter
Factory
spring:
cloud:
gateway:
routes:
- id: ingredients
uri: lb://ingredients
predicates:
- Path=//ingredients/**
filters:
- name: CircuitBreaker
args:
name: fetchIngredients
fallbackUri: forward:/fallback
- id: ingredients-fallback
uri: http://localhost:9994
predicates:
- Path=/fallback
filters:
- name: FallbackHeaders
args:
executionExceptionTypeHeaderName: Test-Header
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
id: ingredients: 这个路由规则的唯一标识符为 "ingredients"。
uri: lb://ingredients: 当请求匹配到该路由规则时,它会被转发到名为 "ingredients" 的服务。"lb://" 是 Spring Cloud Gateway 中定义的 LoadBalancer URI Scheme,表示通过负载均衡方式选择后端服务。
predicates: 这里使用 Path 断言器,表示请求的路径必须以 "/ingredients/" 开头时,才会匹配到这个路由规则。
filters: 这里定义了一个过滤器。
CircuitBreaker: 这是 Spring Cloud Gateway 的熔断器过滤器,它用于实现熔断机制。在这个配置中,指定了一个名为 "fetchIngredients" 的熔断器,当后端服务发生故障时,将会使用 "/fallback" 作为备用的回退 URI。 id: ingredients-fallback: 这个路由规则的唯一标识符为 "ingredients-fallback"。
uri: http://localhost:9994: 当请求匹配到该路由规则时,它会被转发到 "http://localhost:9994",即本地的另一个服务。
predicates: 这里使用 Path 断言器,表示请求的路径必须为 "/fallback" 时,才会匹配到这个路由规则。
filters: 这里定义了一个过滤器。
FallbackHeaders: 这是一个自定义的过滤器,用于在请求发生回退时设置响应头信息。在这个配置中,指定了响应头中的 "Test-Header" 字段为 "executionExceptionTypeHeaderName",用于标识回退的原因类型。
API 网关中的降级和熔断功能主要体现在以下几个方面:
- 降级处理:当后端服务出现异常或不可用时,API 网关可以根据事先定义的降级策略,临时关闭或限制某些功能或服务,以保证核心功能的正常运行。例如,当某个微服务不可用时,可以返回预先设定的默认值或错误信息,而不是将错误暴露给用户。
- 熔断机制:在后端服务出现持续性故障或高错误率时,API 网关可以自动触发熔断机制,暂时关闭对该服务的请求转发,并快速返回预先设定的错误响应。这样可以避免请求大量失败导致雪崩效应,保护其他服务和系统的正常运行。
- 限流控制:API 网关可以根据后端服务的处理能力和负载情况,对请求进行限流控制,防止后端服务被过多请求压垮。通过限流,可以保证服务的稳定性和可用性,防止系统过载。
- 服务降级:当后端服务出现异常或高负载时,API 网关可以通过服务降级,临时关闭或替换某些不重要或资源消耗较大的服务,以保障核心功能的稳定运行。
- 异常处理:API 网关可以对后端服务返回的异常进行统一处理,将错误信息转换成统一的格式,并返回给客户端。这样可以提供更好的用户体验,并帮助开发团队及时发现和解决问题。
# 映射
The MapRequestHeader
GatewayFilter
Factory
如果你的 请求头 里面有 Blue,会把 Blue 的值给 X-Request-Red,相当于做了映射
spring:
cloud:
gateway:
routes:
- id: map_request_header_route
uri: https://example.org
filters:
- MapRequestHeader=Blue, X-Request-Red
2
3
4
5
6
7
8
# 前缀处理器
The PrefixPath
GatewayFilter
Factory
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: https://example.org
filters:
- PrefixPath=/mypath
2
3
4
5
6
7
8
这会将 /mypath
作为所有匹配请求的路径的前缀。因此,对 /hello
的请求将发送到 /mypath/hello
。
# 固定 Host
The PreserveHostHeader
GatewayFilter
Factoryatewayfilter-factory
请求头转发的时候,有时候 host值 会变,这个可以保证不变
spring:
cloud:
gateway:
routes:
- id: preserve_host_route
uri: https://example.org
filters:
# 用于保留请求中的Host头信息。
- PreserveHostHeader
2
3
4
5
6
7
8
9
# 限流
The RequestRateLimiter
GatewayFilter
Factory
一般会使用 redis+令牌桶算法
spring:
cloud:
gateway:
routes:
- id: requestratelimiter_route
uri: https://example.org
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
redis-rate-limiter.requestedTokens: 1
2
3
4
5
6
7
8
9
10
11
12
# 限制请求头大小
RequestHeaderSize
GatewayFilter
Factory
请求保护
spring:
cloud:
gateway:
routes:
- id: requestheadersize_route
uri: https://example.org
filters:
- RequestHeaderSize=1000B
2
3
4
5
6
7
8
# 移除请求头(脱敏)
The RemoveRequestHeader Gateway Filter Factory
spring:
cloud:
gateway:
routes:
- id: removerequestheader_route
uri: https://example.org
filters:
- RemoveRequestHeader=X-Request-Foo
2
3
4
5
6
7
8
This removes the X-Request-Foo
header before it is sent downstream.
这将在发送到下游之前删除 X-Request-Foo
标头。
# 改写特殊的请求参数
The RewritePath
GatewayFilter
Factory
spring:
cloud:
gateway:
routes:
- id: rewritepath_route
uri: https://example.org
predicates:
- Path=/red/**
filters:
- RewritePath=/red/?(?<segment>.*), /$\{segment}
2
3
4
5
6
7
8
9
10
这个配置中定义了一个路由规则,它会将所有以 "/red/" 开头的请求转发到 "https://example.org",并将 "/red/" 后面的路径作为子路径添加到目标 URI 上,实现了路径重写的功能。例如,对于请求 "/red/test",将被转发到 "https://example.org/test"。
# 重试接口
The Retry GatewayFilter
Factory
自动帮你重试接口,降级重试
spring:
cloud:
gateway:
routes:
# 定义一个名为"retry_test"的路由规则
- id: retry_test
# 转发到"http://localhost:8080/flakey"的目标URL
uri: http://localhost:8080/flakey
predicates:
# 当请求的Host为任意子域名.retry.com时,匹配该路由规则
- Host=*.retry.com
filters:
# 定义一个重试过滤器
- name: Retry
args:
# 当请求失败时,最多重试3次(包括初始请求)
retries: 3
# 定义需要重试的HTTP状态码,这里设置为BAD_GATEWAY
statuses: BAD_GATEWAY
# 定义需要重试的HTTP请求方法,这里设置为GET和POST
methods: GET,POST
# 定义重试的时间间隔策略
backoff:
# 第一次重试的时间间隔为10毫秒
firstBackoff: 10ms
# 最大重试时间间隔为50毫秒
maxBackoff: 50ms
# 重试时间间隔的增长因子,每次重试的时间间隔将是上一次的倍数
factor: 2
# 这里设置为false,表示每次重试的时间间隔都是根据初始值来计算的,而不是上一次重试的值
basedOnPreviousValue: false
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
这个配置中定义了一个路由规则,它会将所有 Host 为任意 子域名.retry.com
的请求转发到 "http://localhost:8080/flakey",并在请求失败时进行最多 3 次的重试,重试的时间间隔为 10ms、20ms 和 40ms,最大不超过 50ms,仅对 GET 和 POST 方法的请求进行重试,并且仅在返回的 HTTP 状态码为 BAD_GATEWAY 时进行重试。
目的
- 重试的目的是为了在网络不稳定或服务不可用的情况下,尝试重新发送请求,以增加请求成功的机会。
- 通过重试,可以在一定程度上提高请求的成功率,减少因为临时性的网络问题或服务故障而导致的请求失败。
# 默认过滤器
Default Filters
- 可以用作全局染色
spring:
cloud:
gateway:
default-filters: # 设置默认的过滤器
- AddResponseHeader=X-Response-Default-Red, Default-Blue # 添加响应头的过滤器,给响应头添加 X-Response-Default-Red 和 Default-Blue 两个字段
- PrefixPath=/httpbin # 前缀路径过滤器,给请求路径添加前缀 /httpbin
2
3
4
5
6
全局染色:在这里,它添加了一个名为 X-Response-Default-Red
的响应头,并设置其值为 Default-Blue
。
# 5、其他配置
# 1、全局过滤器
Global Filters
自定义过滤器
@Bean
public GlobalFilter customFilter() {
return new CustomGlobalFilter();
}
public class CustomGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("custom global filter");
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -1;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 2、Http timeouts configuration
Global timeouts
配置 http 超时
spring:
cloud:
gateway:
httpclient:
connect-timeout: 1000
response-timeout: 5s
2
3
4
5
6
# 3、CORS Configuration
跨域配置
spring:
cloud:
gateway:
globalcors: # 全局CORS跨域配置
cors-configurations: # 定义跨域配置
'[/**]': # 匹配所有路径
allowedOrigins: "https://docs.spring.io" # 允许的来源(域名),这里只允许来自 https://docs.spring.io 的请求
allowedMethods: # 允许的HTTP
2
3
4
5
6
7
8
对于所有 GET 请求的路径,允许来自 docs.spring.io
的请求的 CORS(跨源资源共享)请求。