1 微服務(wù)API網(wǎng)關(guān)Gateway
1.1 網(wǎng)關(guān)
1.1.1 簡(jiǎn)介
如果沒有網(wǎng)關(guān),難道不行嗎?功能上是可以的,我們直接調(diào)用提供的接口就可以了。那為什么還需要網(wǎng)關(guān)?
因?yàn)榫W(wǎng)關(guān)的作用不僅僅是轉(zhuǎn)發(fā)請(qǐng)求而已。我們可以試想一下,如果需要做一個(gè)請(qǐng)求認(rèn)證功能,我們可以接入到 API
服務(wù)中。但是倘若后續(xù)又有服務(wù)需要接入,我們又需要重復(fù)接入。這樣我們不僅代碼要重復(fù)編寫,而且后期也不利于維護(hù)。
由于接入網(wǎng)關(guān)后,網(wǎng)關(guān)將轉(zhuǎn)發(fā)請(qǐng)求。所以在這一層做請(qǐng)求認(rèn)證,天然合適。這樣這需要編寫一次代碼,在這一層過濾完畢,再轉(zhuǎn)發(fā)給下面的 API。所以 API 網(wǎng)關(guān)的通常作用是完成一些通用的功能,如請(qǐng)求認(rèn)證,請(qǐng)求記錄,請(qǐng)求限流,黑白名單判斷等。
API網(wǎng)關(guān)
是一個(gè)服務(wù)器,是系統(tǒng)的唯一入口。API網(wǎng)關(guān)
方式的核心要點(diǎn)是,所有的客戶端和消費(fèi)端都通過統(tǒng)一的網(wǎng)關(guān)接入微服務(wù),在網(wǎng)關(guān)層處理所有的非業(yè)務(wù)功能。通常,網(wǎng)關(guān)提供REST/HTTP的訪問API。
總結(jié)主要作用是:
- 反向代理(請(qǐng)求的轉(zhuǎn)發(fā))
- 路由和負(fù)載均衡
- 身份認(rèn)證和權(quán)限控制
- 對(duì)請(qǐng)求限流
1.1.2 相比于Zuul
SpringCloudGateway
基于Spring5
中提供的WebFlux
,是一種響應(yīng)式編程的實(shí)現(xiàn),性能更加優(yōu)越。Zuul
的實(shí)現(xiàn)方式比較老式,基于Servlet
的實(shí)現(xiàn),它是一種阻塞式
編程,在高并發(fā)下性能性能不佳。
1.2 Spring Cloud Gateway介紹
Spring Cloud Gateway
是 Spring Cloud
的新一代API網(wǎng)關(guān),基于WebFlux
框架實(shí)現(xiàn),它旨在為微服務(wù)架構(gòu)提供一種簡(jiǎn)單而有效的統(tǒng)一的API路由管理方式。Spring Cloud Gateway
作為Spring Cloud
生態(tài)系統(tǒng)中的網(wǎng)關(guān),目標(biāo)是替代Netflix ZUUL
,具有更好的性能、更強(qiáng)的擴(kuò)展性、以及更豐富的功能特性,其不僅提供統(tǒng)一的路由方式,并且基于Filter
鏈的方式提供了網(wǎng)關(guān)基本的功能,例如:安全,監(jiān)控/埋點(diǎn),限流等。
1.3 Gateway特性
Spring Cloud Gateway
特性:
- 基于Spring Framework 5, Project Reactor和Spring Boot 2.0
- 動(dòng)態(tài)路由:能夠匹配任何請(qǐng)求屬性
- 可以對(duì)路由指定 Predicate 和 Filter
- 集成
Hystrix
斷路器 - 集成
Spring Cloud DiscoveryClient
服務(wù)發(fā)現(xiàn)功能 - 易于編寫的Predicate和Filter
- 請(qǐng)求限流
- 支持路徑重寫
1.4 Gateway工作流程
客戶端向 Spring Cloud Gateway
發(fā)出請(qǐng)求,然后在Gateway Handler Mapping
中找到與請(qǐng)求相匹配的路由,將其發(fā)送到 Gateway Web Handler
。Handler
再通過指定的過濾器鏈來對(duì)請(qǐng)求進(jìn)行過濾處理,最后發(fā)送到我們實(shí)際的服務(wù)執(zhí)行業(yè)務(wù)邏輯,然后返回。
過濾器鏈被虛線分隔,是因?yàn)檫^濾器既可以在轉(zhuǎn)發(fā)請(qǐng)求前攔截請(qǐng)求,也可以在請(qǐng)求處理之后對(duì)響應(yīng)進(jìn)行攔截處理。
當(dāng)一個(gè)請(qǐng)求到達(dá)Spring Cloud Gateway
時(shí),它的處理順序是:predicates -> uri -> filters
-
predicates
負(fù)責(zé)路由的配置斷言,它可以通過PredicateDefinition
類進(jìn)行配置 -
uri
則是匹配路由的轉(zhuǎn)發(fā)地址,當(dāng)predicates
匹配成功后,URI
則確定了請(qǐng)求的具體處理地址。 - 過濾器(
Filter
)則是對(duì)路由的處理過程進(jìn)行額外的操作,例如添加、刪除或修改請(qǐng)求頭等。這些過濾器可以對(duì)請(qǐng)求和響應(yīng)進(jìn)行處理
1.5 Gateway核心概念
1.5.1 路由
1.5.1.1 定義
路由(Route
)是網(wǎng)關(guān)最基礎(chǔ)的部分,路由信息由一個(gè)ID
,一個(gè)目標(biāo)URI
,一組斷言
和過濾器
組成。
路由斷言Predicate
用于匹配請(qǐng)求,過濾器 Filter
用于修改請(qǐng)求和響應(yīng)。如果斷言為true
,則說明請(qǐng)求URI
和配置匹配,則執(zhí)行路由。
spring:
cloud:
gateway:
# 定義多個(gè)路由
routes:
# 一個(gè)路由route的id
- id: path_route
# 該路由轉(zhuǎn)發(fā)的目標(biāo)URI
uri: https://example.org
# 路由條件集合
predicates:
- Path=/test/**
# 過濾器集合
filters:
- AddRequestHeader=X-Request-Id, 1024
- AddRequestParameter=color, red
1.5.1.2 動(dòng)態(tài)路由
網(wǎng)關(guān)接收外部請(qǐng)求,按照一定的規(guī)則,將請(qǐng)求轉(zhuǎn)發(fā)給其他服務(wù)或者應(yīng)用。如果站在服務(wù)調(diào)用的角度,網(wǎng)關(guān)就扮演著服務(wù)消費(fèi)者的角色,此時(shí),如果再來看看服務(wù)調(diào)用的目標(biāo)URI配置,就會(huì)很自然的發(fā)現(xiàn)一個(gè)問題,服務(wù)提供者調(diào)用的地址是寫死的,即網(wǎng)關(guān)沒有動(dòng)態(tài)的發(fā)現(xiàn)服務(wù),這就涉及到了服務(wù)的自動(dòng)發(fā)現(xiàn)問題,以及發(fā)現(xiàn)服務(wù)后,所涉及到的服務(wù)調(diào)用的負(fù)載均衡的問題。
可以通過Nacos
或者Eureka
注冊(cè)中心動(dòng)態(tài)發(fā)現(xiàn)服務(wù),通過Ribbon
進(jìn)行服務(wù)調(diào)用的負(fù)載均衡。同樣,Gateway
也可以整合Nacos
或者Eureka
,Ribbon
從而實(shí)現(xiàn)動(dòng)態(tài)路由的功能。
想要使用動(dòng)態(tài)路由的功能,首先要整合注冊(cè)中心,這里以Nacos為例
pom依賴
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
配置文件
spring:
application:
name: cloud-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes:
#路由的ID,沒有固定規(guī)則但要求唯一,建議配合服務(wù)名
- id: config_route
#匹配后提供服務(wù)的路由地址, 這里lb之后,跟的是要調(diào)用的服務(wù)名稱
uri: lb://nacos-provider-8002
# 斷言,路徑相匹配的條件
predicates:
- Path=/routeconfig/rest/**
此時(shí),當(dāng)id為config_route
的路由規(guī)則匹配某個(gè)請(qǐng)求后,在調(diào)用該請(qǐng)求對(duì)應(yīng)的服務(wù)時(shí),就會(huì)從nacos注冊(cè)中心自動(dòng)發(fā)現(xiàn)服務(wù),并在服務(wù)調(diào)用的時(shí)候?qū)崿F(xiàn)負(fù)載均衡。
1.5.2 斷言
斷言(Predicate
)參考Java8
中的斷言 Predicate
,用于實(shí)現(xiàn)請(qǐng)求匹配邏輯,例如匹配路徑、請(qǐng)求頭、請(qǐng)求參數(shù)等。請(qǐng)求與斷言匹配則執(zhí)行該路由。
在Gateway
中,有一些的內(nèi)置Predicate Factory
,有了這些Pridicate Factory
,在運(yùn)行時(shí),Gateway
會(huì)自動(dòng)根據(jù)需要?jiǎng)?chuàng)建其對(duì)應(yīng)的 Pridicate
對(duì)象測(cè)試路由條件。
1.5.2.1 默認(rèn)斷言
Gateway提供的斷言有:Path
路由斷言,After
路由斷言,Cookie
路由斷言,Header
路由斷言 ,Host
路由斷言 ,Method
路由斷言等
名稱 | 說明 | 示例 |
---|---|---|
After | 某個(gè)時(shí)間點(diǎn)之后的請(qǐng)求 | - After=2023-08-01T14:31:20.123-07:00[Asia/Shanghai] |
Before | 某個(gè)時(shí)間點(diǎn)之前的請(qǐng)求 | - Before=2023-08-01T14:31:20.123+08:00[Asia/Shanghai] |
Between | 某個(gè)時(shí)間點(diǎn)之中的請(qǐng)求 | - Before=2023-08-01T14:31:20.123+08:00[Asia/Shanghai],2023-08-05T14:31:20.123+08:00[Asia/Shanghai] |
Cookie | 請(qǐng)求必須包含某些cookie | - Cookie=test |
Header | 請(qǐng)求必須包含某些header | - Header=asd,cas |
Host | 請(qǐng)求必須包含某個(gè)host(域名) | - Host=baidu.com,jd.com |
Method | 請(qǐng)求必須是指定方式 | - Method=GET,POST |
Path | 請(qǐng)求路徑必須符合指定規(guī)則 | - Path=/user/{params},/card/** |
Query | 請(qǐng)求參數(shù)必須包含指定參數(shù) | - Query=name,jack |
RemoteAddr | 請(qǐng)求者的ip必須是指定范圍 | - RemoteAddr=192.168.1.1、24 |
Weight | 權(quán)重處理 | - Weight=50 |
Path
路由斷言 Factory
: 根據(jù)請(qǐng)求路徑匹配的路由條件工廠
spring:
cloud:
gateway:
routes:
- id: path_route
uri: https://example.org
predicates:
# 如果可以匹配的PathPattern有多個(gè),則每個(gè)路徑模式以,分開
- Path=/red/{segment},/blue/{segment}
After
路由斷言 Factory
: 在指定日期時(shí)間之后發(fā)生的請(qǐng)求都將被匹配
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
Cookie
路由斷言 Factory
: Cookie
路由斷言 Factory
有兩個(gè)參數(shù),cookie名稱和正則表達(dá)式。請(qǐng)求包含此cookie名稱且正則表達(dá)式為真的將會(huì)被匹配。
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://example.org
predicates:
- Cookie=chocolate, ch.p
Header
路由斷言 Factory
: Header
路由斷言 Factory
有兩個(gè)參數(shù),header
名稱和正則表達(dá)式。請(qǐng)求包含此header名稱且正則表達(dá)式為真的將會(huì)被匹配。
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://example.org
predicates:
- Header=X-Request-Id, \d+
Host
路由斷言 Factory
: Host
路由斷言 Factory
包括一個(gè)參數(shù):host name
列表。使用Ant路徑匹配規(guī)則, .
作為分隔符。
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://example.org
predicates:
- Host=**.somehost.org,**.anotherhost.org
Method
路由斷言 Factory
: Method
路由斷言 Factory
只包含一個(gè)參數(shù):需要匹配的HTTP請(qǐng)求方式
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://example.org
predicates:
- Method=GET
1.5.2.2 自定義Predicate
可以自定義Predicate
來實(shí)現(xiàn)復(fù)雜的路由匹配規(guī)則
實(shí)現(xiàn)自定義 Predicate 工廠
通過HostRoutePredicateFactory創(chuàng)建Predicate進(jìn)行路由判斷
@Component
public class MyHostRoutePredicateFactory extends AbstractRoutePredicateFactory<MyHostRoutePredicateFactory.Config> {
public MyHostRoutePredicateFactory() {
// Config 類作為 Predicate 的配置參數(shù)類
super(Config.class);
}
public static class Config {
// 路由匹配規(guī)則
private String hostName;
public String getHostName() {
return hostName;
}
public void setHostName(String hostName) {
this.hostName = hostName;
}
}
// 生成一個(gè) Predicate 實(shí)例
@Override
public Predicate<ServerWebExchange> apply(Config config) {
// 實(shí)現(xiàn)匹配邏輯
return exchange -> {
// 根據(jù)config實(shí)現(xiàn)匹配判斷
String host = exchange.getRequest().getURI().getHost();
// 匹配配置中的域名
return host.equals(config.getHostName());
};
}
}
使用
RouteLocator locator = new RouteLocatorBuilder(router)
.routes()
.route("test_route", r -> r.path("/test")
.filters(f -> f.filter(new MyHostRoutePredicateFactory.Config("www.test.com")))
.uri("http://localhost:8080"))
.build();
1.5.3 過濾器
過濾器(Filter
) 指的是Spring
框架中GatewayFilter
的實(shí)例,使用過濾器,可以在請(qǐng)求被路由前后對(duì)請(qǐng)求進(jìn)行修改
1.5.3.1 默認(rèn)過濾器
配置文件中添加過濾器 filters
filters:
- AddRequestHeader=name,zs #請(qǐng)求頭添加name:zs
- RemoveRequestHeader=name #移除請(qǐng)求中一個(gè)請(qǐng)求頭
- AddRequestParameter=color,blue #請(qǐng)求參數(shù)添加color:blue
- AddResponseHeader=phone,973345344 #響應(yīng)頭添加phone:973345344
- RemoveResponseHeader=phone,973345344 #響應(yīng)頭移除一個(gè)響應(yīng)頭phone
- PrefixPath=/mypath #添加路徑前綴/mypath
- StripPrefix=n #刪除路徑前綴n個(gè)
- RequestRateLimiter=n #限制請(qǐng)求流量
Spring Cloud Gateway
內(nèi)置的多種過濾器類,例如:
-
AddRequestHeader GatewayFilter
:在請(qǐng)求頭中添加參數(shù) -
PrefixPath GatewayFilter
:請(qǐng)求路徑前綴 -
Hystrix GatewayFilter
:斷路器 -
RateLimit GatewayFilter
:限流 -
Retry GatewayFilter
:重試
1.5.3.2 局部過濾器
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://userservice
predicates:
- Path=/user/**
filters: # 過濾器配置
- AddRequestHeader=token, test # 添加請(qǐng)求頭
上述過濾器的含義:
- 給所有進(jìn)入userservice的請(qǐng)求添加一個(gè)請(qǐng)求頭。
- 請(qǐng)求頭的key為token,value為test。
- 由于當(dāng)前前過濾器寫在微服務(wù)的userservice路由下,因此僅僅對(duì)訪問微服務(wù)userservice的請(qǐng)求有效。
1.5.3.3 全局過濾器
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://userservice
predicates:
- Path=/user/**
default-filters: # 默認(rèn)過濾器配置
- AddRequestHeader=token, test # 添加請(qǐng)求頭
default-filters
的配置和 routes
平級(jí)。
只要配置在 default-filters
下面的過濾器,會(huì)對(duì) routes
配置的所有路由都生效。
1.5.3.4 自定義過濾器
1.5.3.4.1 GatewayFilter
可以通過實(shí)現(xiàn)GatewayFilter
和Ordered
接口自定義Filter
來實(shí)現(xiàn)請(qǐng)求處理邏輯:
@Component
public class TokenFilter implements GatewayFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//請(qǐng)求處理邏輯
log.info("請(qǐng)求路徑:"+ exchange.getRequest().getPath());
ServerHttpRequest request = exchange.getRequest();
MultiValueMap<String, HttpCookie> cookies = request.getCookies();
List<HttpCookie> tokens = cookies.get("access_token");
if (tokens == null || tokens.size() == 0) {
throw new RuntimeException("少了cookie!");
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
當(dāng)有多個(gè)過濾器時(shí),Order
的值決定了過濾器的執(zhí)行順序。
數(shù)值越大優(yōu)先級(jí)越低, 負(fù)的越多, 優(yōu)先級(jí)越高
設(shè)置Order
的值有兩種方式:
- 實(shí)現(xiàn)
Ordered
接口,并且重寫getOrder
方法 - 使用
@Order
注解
1.5.3.4.2 GlobalFilter
public class MyFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//1.獲取請(qǐng)求參數(shù)
//1.這里的request并不是servlet中的request
//2.返回值是一個(gè)多鍵的map集合、也就是說這個(gè)map集合的鍵可以重復(fù)
MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
// 2.獲取userName參數(shù)
String userName = params.getFirst("userName");
// 3.校驗(yàn)
if ("root".equals(userName)) {
// 放行
return chain.filter(exchange);
}
// 4.攔截
// 4.1.禁止訪問,設(shè)置狀態(tài)碼
exchange.getResponse().setStatusCode(500);
// 4.2.結(jié)束處理
return exchange.getResponse().setComplete();
}
}
1.5.3.4.3 AbstractGatewayFilterFactory
通過繼承AbstractGatewayFilterFactory
來實(shí)現(xiàn)
public class DemoGatewayFilterFactory extends AbstractGatewayFilterFactory<DemoGatewayFilterFactory.Config> {
@Override
public GatewayFilter apply(Config config) {
return null;
}
//Config 靜態(tài)內(nèi)部類,負(fù)責(zé)指定網(wǎng)關(guān)的參數(shù)
static class Config{
private String arg1;
}
}
注意
:DemoGatewayFilterFactory
的命名方式:由Demo + GatewayFilterFactory
組成,
其中Demo是自己起名字,GatewayFilterFactory
是固定的。
范型中的Config
是我們待會(huì)要用到的靜態(tài)內(nèi)部類,用于聲明過濾器中傳遞的參數(shù)
在Spring Cloud Gateway
中,自定義的過濾器會(huì)在全局過濾器鏈中生效,不需要顯式配置在路由配置中。這是因?yàn)?code>AbstractGatewayFilterFactory類已經(jīng)實(shí)現(xiàn)了GatewayFilterFactory
接口,并且通過Spring
的自動(dòng)裝配機(jī)制將其注冊(cè)到全局過濾器鏈中,并且可以在任何路由上生效。
需要注意的是,在全局過濾器鏈中的順序是根據(jù)Spring Bean
加載順序決定的??梢酝ㄟ^設(shè)置@Order
注解或?qū)崿F(xiàn)Ordered
接口來控制自定義過濾器在全局過濾器鏈中的順序。
1.5.3.5 過濾路由過濾器的執(zhí)行順序
由上面可知 SpringCloudGateWay
中,有三種過濾器:
- 默認(rèn)過濾器
default-filters
- 只對(duì)具體某個(gè)路由生效的局部過濾器
filters
- 使用
java
代碼編寫的自定義全局過濾器GlobalFilter
過濾器的執(zhí)行順序?yàn)椋耗J(rèn)過濾器 → 當(dāng)前路由過濾器 → 自定義全局過濾器
1.6 實(shí)際操作
1.6.1 pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
1.6.2 啟動(dòng)類
@SpringBootApplication
@EnableEurekaClient
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
1.6.3 配置文件 application.yml
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
# 路由的ID,沒有固定規(guī)則但要求唯一,建議配合服務(wù)名
- id: config_route
# 匹配后提供服務(wù)的路由地址
uri: http://ityouknow.com
# 斷言,路徑相匹配的條件
predicates:
- Path=/routeconfig/rest/**
- id: header_route
uri: http://ityouknow.com
predicates:
- Header=X-Request-Id, \d+
1.7 跨域處理
1.7.1 跨域的概念和原理
跨域:請(qǐng)求位置和被請(qǐng)求位置不同源就會(huì)發(fā)生跨域。
這里的不同源包括兩個(gè)點(diǎn):
- 域名不同:www.baidu.com 和 www.taobao.com。(IP不同也是相同道理)
- 端口不同:127.0.0.1:8080和127.0.0.1:8081。
而瀏覽器又會(huì)禁止請(qǐng)求的發(fā)起者與服務(wù)端發(fā)生跨域AJAX請(qǐng)求。
如果發(fā)生了跨域請(qǐng)求,服務(wù)器端是能夠正常響應(yīng)的,但是響應(yīng)的結(jié)果會(huì)被瀏覽器攔截。
1.7.2 跨域常見解決方案
使用CORS
方式。CORS
是一個(gè)W3C標(biāo)準(zhǔn),全稱是"跨域資源共享"(Cross-origin resource sharing
)。
它允許瀏覽器向跨源服務(wù)器,發(fā)出XMLHttpRequest
請(qǐng)求,從而克服了AJAX
只能同源使用的限制。
1.7.3 gateway中如何解決跨域問題
1.7.3.1 配置文件
配置application.yml文件:
spring:
cloud:
gateway:
globalcors: # 全局的跨域配置
add-to-simple-url-handler-mapping: true # 解決options請(qǐng)求被攔截問題
# options請(qǐng)求 就是一種詢問服務(wù)器是否瀏覽器可以跨域的請(qǐng)求
# 如果每次跨域都有詢問服務(wù)器是否瀏覽器可以跨域?qū)π阅芤彩菗p耗
# 可以配置本次跨域檢測(cè)的有效期maxAge
# 在maxAge設(shè)置的時(shí)間范圍內(nèi),不去詢問,統(tǒng)統(tǒng)允許跨域
corsConfigurations:
'[/**]': # 這里的'/**'表示對(duì)所有路由生效,可以根據(jù)需要調(diào)整為特定路徑
allowedOrigins: # 允許哪些網(wǎng)站的跨域請(qǐng)求
- "http://localhost:8090"
allowedMethods: # 允許的跨域ajax的請(qǐng)求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允許在請(qǐng)求中攜帶的頭信息
allowCredentials: true # 允許在請(qǐng)求中攜帶cookie
maxAge: 360000 # 本次跨域檢測(cè)的有效期(單位毫秒)
# 有效期內(nèi),跨域請(qǐng)求不會(huì)一直發(fā)option請(qǐng)求去增大服務(wù)器壓力
1.7.3.2 編碼
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
@Configuration
public class CorsConfig {
private static final String MAX_AGE = "18000L";
@Bean
public WebFilter corsFilter() {
return (ServerWebExchange ctx, WebFilterChain chain) -> {
ServerHttpRequest request = ctx.getRequest();
// 使用SpringMvc自帶的跨域檢測(cè)工具類判斷當(dāng)前請(qǐng)求是否跨域
if (!CorsUtils.isCorsRequest(request)) {
return chain.filter(ctx);
}
HttpHeaders requestHeaders = request.getHeaders(); // 獲取請(qǐng)求頭
ServerHttpResponse response = ctx.getResponse(); // 獲取響應(yīng)對(duì)象
HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod(); // 獲取請(qǐng)求方式對(duì)象
HttpHeaders headers = response.getHeaders(); // 獲取響應(yīng)頭
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin()); // 把請(qǐng)求頭中的請(qǐng)求源(協(xié)議+ip+端口)添加到響應(yīng)頭中(相當(dāng)于yml中的allowedOrigins)
headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders());
if (requestMethod != null) {
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name()); // 允許被響應(yīng)的方法(GET/POST等,相當(dāng)于yml中的allowedMethods)
}
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true"); // 允許在請(qǐng)求中攜帶cookie(相當(dāng)于yml中的allowCredentials)
headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*"); // 允許在請(qǐng)求中攜帶的頭信息(相當(dāng)于yml中的allowedHeaders)
headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE); // 本次跨域檢測(cè)的有效期(單位毫秒,相當(dāng)于yml中的maxAge)
if (request.getMethod() == HttpMethod.OPTIONS) { // 直接給option請(qǐng)求反回結(jié)果
response.setStatusCode(HttpStatus.OK);
return Mono.empty();
}
return chain.filter(ctx); // 不是option請(qǐng)求則放行
};
}
}
在 Spring-Framework 從 5.3
版本之前,使用以下代碼可以讓 Spring Cloud Gateway
網(wǎng)關(guān)允許跨域:
@Configuration
public class GlobalCorsConfig {
@Bean
public CorsWebFilter corsWebFilter() {
CorsConfiguration config = new CorsConfiguration();
// 這里僅為了說明問題,配置為放行所有域名,生產(chǎn)環(huán)境請(qǐng)對(duì)此進(jìn)行修改
config.addAllowedOrigin("*");
// 放行的請(qǐng)求頭
config.addAllowedHeader("*");
// 放行的請(qǐng)求類型,有 GET, POST, PUT, DELETE, OPTIONS
config.addAllowedMethod("*");
// 暴露頭部信息
config.addExposedHeader("*");
// 是否允許發(fā)送 Cookie
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
而 Spring-Framework 5.3
版本之后,關(guān)于 CORS
跨域配置類 CorsConfiguration
中將 addAllowedOrigin
方法名修改為 addAllowedOriginPattern
,因此配置了變成了以下這樣:
@Configuration
public class GlobalCorsConfig {
@Bean
public CorsWebFilter corsWebFilter() {
CorsConfiguration config = new CorsConfiguration();
// 這里僅為了說明問題,配置為放行所有域名,生產(chǎn)環(huán)境請(qǐng)對(duì)此進(jìn)行修改
config.addAllowedOriginPattern("*");
// 放行的請(qǐng)求頭
config.addAllowedHeader("*");
// 放行的請(qǐng)求類型,有 GET, POST, PUT, DELETE, OPTIONS
config.addAllowedMethod("*");
// 暴露頭部信息
config.addExposedHeader("*");
// 是否允許發(fā)送 Cookie
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
1.8 json配置
在 Spring Cloud Gateway
框架中斷言(Predicate
)類型除了 Yaml
文件外還有 JSON
數(shù)據(jù)格式中的配置進(jìn)行整理。其中 JSON
格式數(shù)據(jù)類型在動(dòng)態(tài)路由時(shí)需使用到,如動(dòng)態(tài)讀取數(shù)據(jù)庫(kù)中保存的 JSON
格式的路由數(shù)據(jù)。
如下:Yaml配置文件
spring:
cloud:
gateway:
routes:
- id: path_route
uri: http://127.0.0.1:8080
predicates:
- Path=/server-a/**,/server-b/**
- Method=GET,POST
動(dòng)態(tài)路由的JSON格式
{
"id": "path_route",
"uri": "http://127.0.0.1:8082",
"predicates":[
{
"name": "Path",
"args": {
"_genkey_0": "/server-a/**",
"_genkey_1": "/server-b/**"
}
},{
"name": "Method",
"args": {
"_genkey_0": "GET",
"_genkey_1": "POST"
}
]
}
其中args
中值對(duì)應(yīng)的key
,即_genkey_0
可以任意定義值,查看源碼讀取該配置可知,源碼通過args.values()
獲取配置的所有值,所以此處key
的命名可任意命名。文章來源:http://www.zghlxwxcb.cn/news/detail-625203.html
參考連接:
https://mp.weixin.qq.com/s/LY66FPCajHkzXJUFlfSNYg
https://blog.csdn.net/h1774733219/article/details/124384527
https://blog.csdn.net/qq_46203643/article/details/127150590
https://mp.weixin.qq.com/s/Y6M1GzrIOBOz6fM12b0uPw文章來源地址http://www.zghlxwxcb.cn/news/detail-625203.html
到了這里,關(guān)于SpringCloud之微服務(wù)API網(wǎng)關(guān)Gateway介紹的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!