<!-- Gatway 閘道器會和springMvc衝突,不能新增web依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- gateway 依賴 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
# 埠
server:
port: 9606
# 服務名
spring:
application:
name: kgcmall-gatway
cloud:
#nacos 設定
nacos:
discovery:
server-addr: 127.0.0.1:8848
# 閘道器設定
gateway:
routes: # 路由,是list集合,可以設定多個路由
#product模組
- id: kh96_route_first # 當前route路由的唯一標識,不能重複
#uri: http://localhost:9602 # 路由轉發的目標資源地址,不支援多負載呼叫,不利於擴充套件,不推薦
uri: lb://kgcmall96-prod # lb 從nacos註冊中心的服務列表中,根據指定的服務名,呼叫服務,推薦用法
predicates: # 指定路由斷言設定,支援多個斷言,只要斷言成功(滿足路由轉發條件),才會執行轉發到目標資源地址存取
- Path=/prod-gateway/** # 指定path路徑斷言,必須滿足請求地址是/prod-gateway開始,才會執行路由轉發
filters: # 指定路由過濾設定,支援多個過濾器,在斷言成功,執行路由轉發時,對請求和響應資料進行過濾處理
- StripPrefix=1 # 在請求斷言成功後,執行路由轉發時,自動去除第一層的存取路徑/prod-gateway
#user模組
- id: kh96_route_second
uri: lb://kgcmall96-user
predicates:
- Path=/user-gateway/**
filters:
- StripPrefix=1
Route 主要由 路由id、目標uri、斷言集合和過濾器集合組成,那我們簡單看看這些屬性到底有什麼作用。
(1)id:路由標識,要求唯一,名稱任意(預設值 uuid,一般不用,需要自定義);
(2)uri:請求最終被轉發到的目標地址;
(3)order: 路由優先順序,數位越小,優先順序越高;
(4)predicates:斷言陣列,即判斷條件,如果返回值是boolean,則轉發請求到 uri 屬性指定的服務中;
(5)filters:過濾器陣列,在請求傳遞過程中,對請求做一些修改;
Predicate(斷言, 謂詞) 用於進行條件判斷,只有斷言都返回真,才會真正的執行路由。
斷言就是說: 在什麼條件下 才能進行路由轉發;
基於Datetime型別的斷言工廠
AfterRoutePredicateFactory
: 接收一個日期引數,判斷請求日期是否晚於指定日期;
BeforeRoutePredicateFactory
: 接收一個日期引數,判斷請求日期是否早於指定日期;BetweenRoutePredicateFactory
: 接收兩個日期引數,判斷請求日期是否在指定時間段內;基於遠端地址的斷言工廠RemoteAddrRoutePredicateFactory
基於Cookie的斷言工廠CookieRoutePredicateFactory
(接收兩個引數,cookie 名字和一個正規表示式)
基於Header的斷言工廠HeaderRoutePredicateFactory
基於Host的斷言工廠HostRoutePredicateFactory
基於Method請求方法的斷言工廠MethodRoutePredicateFactory
基於Path求路徑的斷言工廠
PathRoutePredicateFactory
(接收一個引數,判斷請求的URI部分是否滿足路徑規則)
基於路由權重的斷言工廠WeightRoutePredicateFactory
(接收一個[組名,權重], 然後對於同一個組內的路由按照權重轉發)
routes:
-id: weight_route1 uri: host1 predicates:
-Path=/product/**
-Weight=group3, 1
-id: weight_route2 uri: host2 predicates:
-Path=/product/**
-Weight= group3, 9
/**
* Created On : 28/11/2022.
* <p>
* Author : huayu
* <p>
* Description: 自定義閘道器斷言工廠-許可權斷言
*/
@Component //自定義斷言工廠,必須是一個元件放入容器才可以生效
public class MyAuthRoutePredicateFactory
extends AbstractRoutePredicateFactory<MyAuthRoutePredicateFactory.Config> {
/*
設定項名MyAuth的設定引數值,對映到斷言工廠內部類的屬性名
*/
public static final String MYAUTH_KEY = "myAuth";
/*
通過空參構造方法,指定靜態內部類,用於接收組態檔中的設定項的內容,即斷言(- myAuth=xxx)
*/
public MyAuthRoutePredicateFactory() {
super(MyAuthRoutePredicateFactory.Config.class);
}
/*
價格核心組態檔中的自定義設定項的內容,對映到當前設定類的屬性中,即Collections.singletonList(DATETIME_KEY);指定的內部屬性
*/
public List<String> shortcutFieldOrder() {
return Collections.singletonList(MYAUTH_KEY);
}
@Override
public Predicate<ServerWebExchange> apply(MyAuthRoutePredicateFactory.Config config) {
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange serverWebExchange) {
//自定義許可權斷言業務邏輯
if (StringUtils.isNotBlank(config.getMyAuth())) {
//判斷自定義許可權設定引數值,是否和當前指定的值一直,如果一致則斷言成功,否則失敗
return config.getMyAuth().equals("KH96");
}
//斷言失敗
return false;
}
};
}
@Data
public static class Config {
private String myAuth;
}
}
可以設定多個引數,案例:https://blog.csdn.net/qq_31155349/article/details/108557969
predicates:
- MyAuth=KH96 # 自定義 許可權斷言 設定 (注意首字母要大寫)
測試效果:
predicates:
- MyAuth=KHxx # 設定錯誤資訊
測試效果 :
1 作用: 過濾器就是在請求的傳遞過程中,對請求和響應做一些手腳
2 生命週期: Pre Post
3 分類: 區域性過濾器(作用在某一個路由上) 全域性過濾器(作用全部路由上)
在Gateway中, Filter的生命週期只有兩個:「pre」 和 「post」。
PRE: 這種過濾器在請求被路由之前呼叫。我們可利用這種過濾器實現身份驗證、在叢集中選擇請求的微服務、記錄偵錯資訊等。
POST:這種過濾器在路由到微服務以後執行。這種過濾器可用來為響應新增標準的HTTP Header、收集統計資訊和指標、將響應從微服務傳送給使用者端等。
Gateway 的Filter從作用範圍可分為兩種: GatewayFilter與GlobalFilter。
過濾器工廠 | 作用 | 引數 |
---|---|---|
AddRequestHeader | 為原始請求新增Header | Header的名稱及值 |
AddRequestParameter | 為原始請求新增請求引數 | 引數名稱及值 |
AddResponseHeader | 為原始響應新增Header | Header的名稱及值 |
DedupeResponseHeader | 剔除響應頭中重複的值 | 需要去重的Header名稱及去重策略 |
Hystrix | 為路由引入Hystrix的斷路器保護 HystrixCommand的名稱 | |
FallbackHeaders | 為fallbackUri的請求頭中新增具體的異常資訊 | Header的名稱 |
PrefixPath | 為原始請求路徑新增字首 | 字首路徑 |
PreserveHostHeader | 為請求新增一個preserveHostHeader=true的屬性,路由過濾器會檢查該屬性以決定是否要傳送原始的Host | 無 |
RequestRateLimiter | 用於對請求限流,限流演演算法為令牌桶 | keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus |
RedirectTo | 將原始請求重定向到指定的URL | http狀態碼及重定向的url |
RemoveHopByHopHeadersFilter | 為原始請求刪除IETF組織規定的一系列Header | 預設就會啟用,可以通過設定指定僅刪除哪些Header |
RemoveRequestHeader | 為原始請求刪除某個Header | Header名稱 |
RemoveResponseHeader | 為原始響應刪除某個Header | Header名稱 |
RewritePath | 重寫原始的請求路徑 | 原始路徑正規表示式以及重寫後路徑的正規表示式 |
RewriteResponseHeader | 重寫原始響應中的某個Header | Header名稱,值的正規表示式,重寫後的值 |
SaveSession | 在轉發請求之前,強制執行WebSession::save操作 | 無 |
secureHeaders | 為原始響應新增一系列起安全作用的響應頭 | 無,支援修改這些安全響應頭的值 |
SetPath | 修改原始的請求路徑 | 修改後的路徑 |
SetResponseHeader | 修改原始響應中某個Header的值 | Header名稱,修改後的值 |
SetStatus | 修改原始響應的狀態碼 | HTTP 狀態碼,可以是數位,也可以是字串 |
StripPrefix | 用於截斷原始請求的路徑 | 使用數位表示要截斷的路徑的數量 |
Retry | 針對不同的響應進行重試 | retries、statuses、methods、series |
RequestSize | 設定允許接收最大請求包的大小。如果請求包大小超過設定的值,則返回 413 Payload Too Large | 請求包大小,單位為位元組,預設值為5M |
ModifyRequestBody | 在轉發請求之前修改原始請求體內容 | 修改後的請求體內容 |
ModifyResponseBody | 修改原始響應體的內容 | 修改後的響應體內容 |
Default | 為所有路由新增過濾器 | 過濾器工廠名稱及值 |
簡單測試幾個;
filters:
- AddRequestHeader=X-Request-token,token_kh96 # 新增請求頭引數,兩個引數,第一個是新增請求頭引數名,第二個引數值
@GetMapping("/mallProduct")
public KgcMallProduct mallProduct(@RequestParam Integer pid,
@RequestHeader(value = "X-Request-token", required = false) String gateWayFilterToken) {
log.info("------ 根據商品編號:{}, 查詢商品詳情 ------", pid);
//通過GateWay閘道器濾器,增加請求頭引數
log.info("------ 通過GateWay閘道器濾器,增加請求頭引數 X-Request-token:{} ------", gateWayFilterToken);
// 呼叫業務介面,查詢商品詳情
return kgcMallProductService.getMallProductById(pid);
}
filters:
- AddRequestParameter=queryName,param_kh96 # 新增普通 請求引數 ,兩個引數,第一個是新增請求頭引數名,第二個引數值
@GetMapping("/mallProduct")
public KgcMallProduct mallProduct(@RequestParam Integer pid
@RequestParam(value = "queryName", required = false) String gateWayFilterParamQueryName) {
log.info("------ 根據商品編號:{}, 查詢商品詳情 ------", pid);
//通過GateWay閘道器濾器,增加請求引數
log.info("------ 通過GateWay閘道器濾器,增加請求頭引數 queryName:{} ------", gateWayFilterParamQueryName);
// 呼叫業務介面,查詢商品詳情
return kgcMallProductService.getMallProductById(pid);
}
如果帶checkParam引數進行引數校驗,如果沒有攜帶直接放行;
/**
* Created On : 28/11/2022.
* <p>
* Author : huayu
* <p>
* Description: 自定義閘道器過濾工廠 -校驗過濾器
*/
@Component //自定義閘道器過濾工廠,必須是一個元件放入容器才可以生效
public class MyCheckGatewayFilterFactory extends AbstractGatewayFilterFactory<MyCheckGatewayFilterFactory.Config> {
/*
設定項名MyAuth的設定引數值,對映到斷言工廠內部類的屬性名
*/
public static final String MYCHECK_KEY = "myCheck";
public MyCheckGatewayFilterFactory() {
super(MyCheckGatewayFilterFactory.Config.class);
}
public List<String> shortcutFieldOrder() {
return Arrays.asList(MYCHECK_KEY);
}
public GatewayFilter apply(MyCheckGatewayFilterFactory.Config config) {
return new GatewayFilter() {
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//自定義閘道器過濾器實現過濾業務邏輯
//模擬傳送到閘道器的請求,如果請求攜帶引數和閘道器過濾器設定引數一致,放行,如果不一致,直接404
String checkParam = exchange.getRequest().getQueryParams().getFirst("checkParam");
//如果攜帶了checkParam引數 判斷獲取的請求引數是否和閘道器過濾器中設定的引數一致
if (StringUtils.isNotBlank(checkParam)) {
//判斷是否一致
if (checkParam.equals(config.getMyCheck())) {
//過濾器放行到目標請求
return chain.filter(exchange);
}
//返回404
exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
return exchange.getResponse().setComplete();
}
//沒有攜帶引數,直接放行
return chain.filter(exchange);
}
};
}
@Data
public static class Config {
private String myCheck;
}
}
攜帶token放行,不攜帶,跳轉到登入頁面(通過跳轉到百度模擬)
/**
* Created On : 28/11/2022.
* <p>
* Author : huayu
* <p>
* Description: 自定義閘道器全域性過濾,實現token鑑權,實現GlobalFilter 和 Ordered介面
*/
@Component //必須是spring元件
public class MyTokenGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//自定義全域性過濾邏輯,判斷請求頭中們是否攜帶了token引數,如果帶了就放行,非歐洲就就拒絕
if (StringUtils.isBlank(exchange.getRequest().getHeaders().getFirst("token"))) {
//沒有攜帶token,直接重定向到登入頁
//模擬重定向到單點登入網址,臨時用百度
String redirectUrl = "https://www.baidu.com";
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.SEE_OTHER); //303 錯誤,重定向
response.getHeaders().set(HttpHeaders.LOCATION, redirectUrl);
//結束響應
return response.setComplete();
}
//代了token,
//TODO token校驗
return chain.filter(exchange);
}
@Override
public int getOrder() {
//指定全域性過濾器的優先順序,值越小,優先順序越高
return 0;
}
}