過濾器的作用:過濾器就是在請求的過程中,對請求和響應做一些手腳
生命周期:Pre Post
分類:局部過濾器(作用在某一個路由上) 全局過濾器(作用在全部路由上)
在Gateway中,Filter的生命周期隻有兩個:“pre”和“post”。
- PRE:這種過濾器在請求被路由之前調用。我們可利用這種過濾器實作身份驗證、在叢集中選擇請求的微服務、記錄調試資訊等。
- POST:這種過濾器在路由到微服務以後執行。這種過濾器可用來為響應添加标準的HTTP Header、收集統計資訊和名額、将響應從微服務發送給用戶端等。
目錄
1 局部過濾器
1.1 内置局部過濾器
1.2 内置過濾器的使用
1.3 自定義局部過濾器
2 全局過濾器
2.1 内置的全局過濾器
2.2 自定義全局過濾器
1 局部過濾器
局部過濾器是針對單個路由的過濾器。
1.1 内置局部過濾器
在SpringCloud Gateway中内置了很多不同類型的網關路由過濾器。具體如下:
1.2 内置過濾器的使用
下面示例SetStatus過濾器工廠的使用:
第一步:配置檔案中加入過濾器
server:
port: 7000
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos服務端位址
gateway:
discovery:
locator:
enabled: true # 啟用探測器,讓gateway可以發現nacos中的微服務
routes: # 路由數組(路由:就是當指定請求滿足什麼條件的時候,轉發到哪個微服務)
- id: product_route # 目前路由的辨別,要求唯一。預設uuid
uri: lb://service-product # lb指的是負載均衡(load balancing),service-product是nacos中微服務的名稱
order: 1 # 路由的優先級,數字越小級别越高
predicates: # 斷言(就是路由轉發要滿足的條件)
- Path=/product-serv/** # 當請求路徑滿足Path指定的規則時,才進行路由轉發
filters: # 過濾器,請求在傳遞過程中可以通過過濾器對其進行一定的修改
- StripPrefix=1 # 轉發之前去掉1層路徑
- SetStatus=250 # 修改原始響應的狀态碼
第二步:啟動測試
1.3 自定義局部過濾器
需求:通過過濾器,配置是否在控制台輸出日志資訊,以及是否記錄日志。
第一步:在配置檔案中,添加一個Log的過濾器配置
server:
port: 7000
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos服務端位址
gateway:
discovery:
locator:
enabled: true # 啟用探測器,讓gateway可以發現nacos中的微服務
routes: # 路由數組(路由:就是當指定請求滿足什麼條件的時候,轉發到哪個微服務)
- id: product_route # 目前路由的辨別,要求唯一。預設uuid
uri: lb://service-product # lb指的是負載均衡(load balancing),service-product是nacos中微服務的名稱
order: 1 # 路由的優先級,數字越小級别越高
predicates: # 斷言(就是路由轉發要滿足的條件)
- Path=/product-serv/** # 當請求路徑滿足Path指定的規則時,才進行路由轉發
filters: # 過濾器,請求在傳遞過程中可以通過過濾器對其進行一定的修改
- StripPrefix=1 # 轉發之前去掉1層路徑
- Log=true,false # 控制日志是否開啟
第二步:自定義一個過濾器工廠,實作裡面的方法
package cn.jack.filter;
import lombok.Data;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class LogGatewayFilterFactory extends AbstractGatewayFilterFactory<LogGatewayFilterFactory.Config>{
public LogGatewayFilterFactory() {
super(Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("consoleLog", "cacheLog");
}
@Override
public GatewayFilter apply(Config config) {
return ((exchange, chain) -> {
if (config.consoleLog) {
System.out.println("console日志已開啟...");
}
if (config.cacheLog) {
System.out.println("cache日志已開啟...");
}
return chain.filter(exchange);
});
}
@Data
public static class Config{
private boolean consoleLog;
private boolean cacheLog;
}
}
第三步:運作測試
2 全局過濾器
全局過濾器作用于所有路由,無需配置。通過全局過濾器可以實作對權限的統一校驗,安全性驗證等功能。
2.1 内置的全局過濾器
SpringCloud Gateway内部也是通過一系列的内置全局過濾器對整個路由轉發進行處理的。
2.2 自定義全局過濾器
示例通過自定義全局過濾器,完成統一的權限校驗。
開發中的鑒權邏輯:
- 當用戶端第一次請求服務時,服務端對使用者進行資訊認證(登入)
- 認證通過,将使用者資訊進行加密形成token,傳回給用戶端,作為登入憑證
- 以後每次請求,用戶端都攜帶認證的token
- 服務端對token進行解密,判斷是否有效。
如下圖,對于驗證使用者是否已經登入及鑒權的過程,可以在網關統一校驗。
校驗的标準就是請求中是否攜帶token憑證,以及token的正确性。
下面我們通過自定義一個GlobalFIlter,去校驗所有請求的請求參數中是否包含“token”,如果不包含請求參數“token”則不轉發路由,否則執行正常邏輯。
package cn.jack.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.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 自定義全局過濾器,需要實作GlobalFilter和Ordered接口
*/
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
// 完成鑒權邏輯
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getQueryParams().getFirst("token");
if (StringUtils.isEmpty(token)) {
System.out.println("鑒權失敗。确少token參數。");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
if (!"jack".equals(token)) {
System.out.println("token無效...");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 繼續執行filter鍊
return chain.filter(exchange);
}
// 順序,數值越小,優先級越高
@Override
public int getOrder() {
return 0;
}
}
啟動測試: