Lison
<dreamlison@163.com>
, v1.0.0
, 2023.10.20
Spring Cloud Alibaba-05-Gateway網關-02-斷言(Predicate)使用
文章目錄
- Spring Cloud Alibaba-05-Gateway網關-02-斷言(Predicate)使用
- 通過時間匹配
- 通過 Cookie 匹配
- 通過 Header 匹配
- 通過 Host 匹配
- 通過請求方式匹配
- 通過請求路徑匹配
- 通過請求參數匹配
- 通過請求 ip 地址進行匹配
- 自定義斷言
- 如何自定義路由斷言
- 實現
- 組合使用
Predicate 斷言,用于進行條件判斷,只有斷言都為真,才會真正的執行路由。
通過時間匹配
Predicate 支持設置一個時間,在請求進行轉發的時候,可以通過判斷在這個時間之前或者之后進行轉發。比如我們現在設置只有在 2023年 10 月 20 日才會轉發到我的網站,在這之前不進行轉發,我就可以這樣配置:
spring:cloud:gateway:routes:- id: spring_serviceuri: http://localhost:18001predicates:- After=2023-10-20T08:30:00+08:00[Asia/Shanghai]
Spring 是通過 ZonedDateTime 來對時間進行的對比,ZonedDateTime 是 Java 8 中日期時間功能里,用于表示帶時區的日期與時間信息的類,ZonedDateTime 支持通過時區來設置時間,中國的時區是:Asia/Shanghai。
After Route Predicate 是指在這個時間之后的請求都轉發到目標地址。上面的示例是指,請求時間在 2023 年 10 月 20 日 08 點 30 分 0 秒之后的所有請求都轉發到地址http://localhost:18001。+08:00是指時間和 UTC 時間相差八個小時,時間地區為Asia/Shanghai。
添加完路由規則之后,訪問地址http://localhost:18001/spring_service 會自動轉發到http://localhost:18001。
Before Route Predicate 剛好相反,在某個時間之前的請求的請求都進行轉發。我們把上面路由規則中的 After 改為 Before,如下:
spring:cloud:gateway:routes:- id: spring_serviceuri: http://localhost:18001predicates:- Before=2023-10-20T08:30:00+08:00[Asia/Shanghai]
就表示在這個時間之前可以進行路由,在這時間之后停止路由,修改完之后重啟項目再次訪問地址http://localhost:18001/spring_service
,頁面會報 404 沒有找到地址。
除過在時間之前或者之后外,Gateway 還支持限制路由請求在某一個時間段范圍內,可以使用 Between Route Predicate 來實現
spring:cloud:gateway:routes:- id: spring_serviceuri: http://localhost:18001predicates:- Between=2023-10-20T08:30:00+08:00[Asia/Shanghai],2024-10-20T08:30:00+08:00[Asia/Shanghai]
這樣設置就意味著在這個時間段內可以匹配到此路由,超過這個時間段范圍則不會進行匹配。通過時間匹配路由的功能很酷,可以用在限時搶購的一些場景中。
通過 Cookie 匹配
spring:cloud:gateway:routes:- id: spring_serviceuri: http://localhost:18001predicates:- Cookie=token,lison
總結:去掉Cookie或Cookie不正確,后臺匯報 404 錯誤。帶上正確的Cookie正常訪問
通過 Header 匹配
Header Route Predicate 和 Cookie Route Predicate 一樣,也是接收 2 個參數,一個 header 中屬性名稱和一個正則表達式,這個屬性值和正則表達式匹配則執行。
spring:cloud:gateway:routes:- id: spring_serviceuri: http://localhost:18001predicates:- Header=X-Request-Id, \d+
總結:去掉Header或Header不合法,后臺匯報 404 錯誤。帶上合法的Header正常訪問
通過 Host 匹配
Host Route Predicate 接收一組參數,一組匹配的域名列表,這個模板是一個 ant 分隔的模板,用.
號作為分隔符。它通過參數中的主機地址作為匹配規則。
spring:cloud:gateway:routes:- id: spring_serviceuri: http://localhost:18001predicates:- Host=**.lison.com
經測試以上兩種 host 均可匹配到 host_route 路由,去掉 host 參數則會報 404 錯誤。
通過請求方式匹配
可以通過是 POST、GET、PUT、DELETE 等不同的請求方式來進行路由。
spring:cloud:gateway:routes:- id: spring_serviceuri: http://localhost:18001predicates:- Method=GET
測試返回頁面代碼,證明匹配到路由,我們再以 POST 的方式請求測試。返回 404 沒有找到,證明沒有匹配上路由
通過請求路徑匹配
Path Route Predicate 接收一個匹配路徑的參數來判斷是否走路由。
spring:cloud:gateway:routes:- id: spring_serviceuri: http://localhost:18001predicates:- Path=/spring_service/{segment} # - Path=/spring_service/**
如果請求路徑符合要求,則此路由將匹配,例如:/spring_service/1 或者 /spring_service/bar。
測試:
http://localhost:18003/spring_service/1
http://localhost:18003/spring_service/2
http://localhost:18003/spring_xxx/2
經過測試第一和第二條命令可以正常獲取到頁面返回值,最后一個命令報 404,證明路由是通過指定路由來匹配。
通過請求參數匹配
Query Route Predicate 支持傳入兩個參數,一個是屬性名一個為屬性值,屬性值可以是正則表達式。
spring:cloud:gateway:routes:- id: spring_serviceuri: http://localhost:18001predicates:- Query=token
這樣配置,只要請求中包含 token 屬性的參數即可匹配路由。
http://localhost:18003/spring_service/getServerProd?token=dfasdfas&id=xxx
經過測試發現只要請求匯總帶有 smile 參數即會匹配路由,不帶 token 參數則不會匹配。
還可以將 Query 的值以鍵值對的方式進行配置,這樣在請求過來時會對屬性值和正則進行匹配,匹配上才會走路由。
spring:cloud:gateway:routes:- id: spring_serviceuri: http://localhost:18001predicates:- Query=token,lison.
這樣只要當請求中包含 keep 屬性并且參數值是以 lison 開頭的長度為六位的字符串才會進行匹配和路由。
http://localhost:18003/spring_service/getServerProd?token=lison6&id=xxx
測試可以返回頁面代碼,將 token 的屬性值改為 pubx 再次訪問就會報 lison66, 證明路由需要匹配正則表達式才會進行路由。
通過請求 ip 地址進行匹配
Predicate 也支持通過設置某個 ip 區間號段的請求才會路由,RemoteAddr Route Predicate 接受 cidr 符號 (IPv4 或 IPv6) 字符串的列表(最小大小為 1),例如 192.168.0.1/16 (其中 192.168.0.1 是 IP 地址,16 是子網掩碼)。
spring:cloud:gateway:routes:- id: spring_serviceuri: http://localhost:18001predicates:- RemoteAddr=192.168.1.1/24
可以將此地址設置為本機的 ip 地址進行測試。
如果請求的遠程地址是 192.168.1.10,則此路由將匹配。
自定義斷言
如何自定義路由斷言
我們可以查看內置的斷言如何實現?舉例:BetweenRoutePredicateFactory
//斷言工廠的類的命名規則為XXXXRoutePredicateFactory 需要 繼承AbstractRoutePredicateFactorypublic class BetweenRoutePredicateFactory extends AbstractRoutePredicateFactory<Config> {public static final String DATETIME1_KEY = "datetime1";public static final String DATETIME2_KEY = "datetime2";public BetweenRoutePredicateFactory() {super(Config.class);}public List<String> shortcutFieldOrder() {return Arrays.asList("datetime1", "datetime2"); //與配置文件映射}//判斷邏輯方法public Predicate<ServerWebExchange> apply(final Config config) {Assert.isTrue(config.getDatetime1().isBefore(config.getDatetime2()), config.getDatetime1() + " must be before " + config.getDatetime2());return new GatewayPredicate() {//核心判斷邏輯public boolean test(ServerWebExchange serverWebExchange) {ZonedDateTime now = ZonedDateTime.now();return now.isAfter(config.getDatetime1()) && now.isBefore(config.getDatetime2());}public String toString() {return String.format("Between: %s and %s", config.getDatetime1(), config.getDatetime2());}};}//參數配置類@Validatedpublic static class Config {private @NotNull ZonedDateTime datetime1;private @NotNull ZonedDateTime datetime2;public Config() {}public ZonedDateTime getDatetime1() {return this.datetime1;}public Config setDatetime1(ZonedDateTime datetime1) {this.datetime1 = datetime1;return this;}public ZonedDateTime getDatetime2() {return this.datetime2;}public Config setDatetime2(ZonedDateTime datetime2) {this.datetime2 = datetime2;return this;}}
}
知道內置路由斷言的實現細節,我們只需要按照它的實現方式,來按部就班的實現自己的路由斷言即可。
實現
需求假設:年齡大于18歲,小于60歲的可以訪問
1、先進行路由設置
spring:cloud:gateway:routes:- id: spring_serviceuri: http://localhost:18001predicates:- Age=18,60 #年齡大于18歲,小于60歲的可以訪問
2、自定義實現路由,根據前面的配置我們知道規則名為Age,那么我們的自定義的斷言名稱必須是AgeRoutePredicateFactory
package com.lison.springcloudservice.config.predicates;/*** @className: com.lison.springcloudservice.config.predicates-> AgeRoutePredicateFactory* @description: 自定義的斷言工廠 1.名稱必須是配置+RoutePredicateFactory 2.必須繼承AbstractRoutePredicateFactory<配置類>* @author: Lison* @createDate: 2023-10-20*/
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;@Component
public class AgeRoutePredicateFactory extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config> {public AgeRoutePredicateFactory() {super(AgeRoutePredicateFactory.Config.class);}//讀取配置文件的參數值,賦值到配置類中的屬性上@Overridepublic List<String> shortcutFieldOrder() {//順序必須與yml文件中的配置順序對應return Arrays.asList("minAge", "maxAge");}@Overridepublic Predicate<ServerWebExchange> apply(AgeRoutePredicateFactory.Config config) {return new Predicate<ServerWebExchange>() {@Overridepublic boolean test(ServerWebExchange serverWebExchange) {//serverWebExchange很強大,可以可以獲取到很多內容String ageStr = serverWebExchange.getRequest().getQueryParams().getFirst("age");if (StringUtils.isNotEmpty(ageStr)) {int age = Integer.parseInt(ageStr);return age > config.getMinAge() && age < config.getMaxAge();}return false;}};}//用于接收參數public static class Config {private int minAge;private int maxAge;public int getMinAge() {return minAge;}public void setMinAge(int minAge) {this.minAge = minAge;}@Overridepublic String toString() {return "Config{" +"minAge=" + minAge +", maxAge=" + maxAge +'}';}public int getMaxAge() {return maxAge;}public void setMaxAge(int maxAge) {this.maxAge = maxAge;}}
}
3、重啟服務網關,測試自定義的路由斷言,age=20可以訪問,而age=15則無法訪問
組合使用
上面為了演示各個 Predicate 的使用,我們是單個單個進行配置測試,其實可以將各種 Predicate 組合起來一起使用。
spring:cloud:gateway:routes:- id: spring_serviceuri: http://localhost:18001predicates:- Host=**.lison.com- Path=/spring_service/** # 當請求路徑滿足Path指定的規則時,才進行路由轉發- Cookie=token,lison- After=2023-10-20T08:30:00+08:00[Asia/Shanghai]- Before=2023-10-20T08:30:00+08:00[Asia/Shanghai]- Between=2023-10-20T08:30:00+08:00[Asia/Shanghai],2024-10-20T08:30:00+08:00[Asia/Shanghai]- Header=X-Request-Id, \d+- Host=**.lison.com- Method=GET,POST- Query=token,lison.- RemoteAddr=192.168.1.1/24- Age=18,60
各種 Predicates 同時存在于同一個路由時,請求必須同時滿足所有的條件才被這個路由匹配。
一個請求滿足多個路由的謂詞條件時,請求只會被首個成功匹配的路由轉發