前言
即使Spring Cloud Gateway
自帶有許多實用的GatewayFilter Factory、Gateway Filter、Global Filter
,但是在很多情景下我們仍然希望可以自定義自己的過濾器。實現一些騷操作。所以自定義過濾器就顯得非常有必要。本文主要介紹了自定義Gateway Filter
、自定義Global Filter
、自定義Gateway Filter Factory
。
案例
實現自己的過濾器我們其實可以去檢視Spring Cloud Gateway
自帶過濾器原始碼是如何實現的,
自定義Gateway Filter
實現自定義的Gateway Filter
我們需要GatewayFilter、Ordered
兩個介面
/**
* 此過濾器功能為計算請求完成時間
*/
public class MyFilter implements GatewayFilter, Ordered {
private static final String ELAPSED_TIME_BEGIN = "elapsedTimeBegin";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
exchange.getAttributes().put(ELAPSED_TIME_BEGIN, System.currentTimeMillis());
return chain.filter(exchange).then(
Mono.fromRunnable(() -> {
Long startTime = exchange.getAttribute(ELAPSED_TIME_BEGIN);
if (startTime != null) {
System.out.println(exchange.getRequest().getURI().getRawPath() + ": " + (System.currentTimeMillis() - startTime) + "ms");
}
})
);
}
/*
*過濾器存在優先順序,order越大,優先順序越低
*/
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
複製程式碼
定義好MyFilter
以後,其需要跟Route
繫結使用,不能在application.yml
檔案中配置使用
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes().route(r ->
r.path("/aa")
//轉發路由
.uri("http://localhost:8003/provider/test")
//註冊自定義過濾器
.filters(new MyFilter())
//給定id
.id("user-service"))
.build();
}
複製程式碼
測試結果:可以在控制檯看到請求響應時間。
自定義Gateway Filter Factory
很多時候我們更希望在配置檔案中配置Gateway Filter
,所以我們可以自定義過濾器工廠實現。
自定義過濾器工廠需要繼承AbstractGatewayFilterFactory
@Component
public class AuthorizeGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthorizeGatewayFilterFactory.Config> {
private static final Log logger = LogFactory.getLog(AuthorizeGatewayFilterFactory.class);
private static final String AUTHORIZE_TOKEN = "token";
private static final String AUTHORIZE_UID = "uid";
@Autowired
private StringRedisTemplate stringRedisTemplate;
public AuthorizeGatewayFilterFactory() {
super(Config.class);
logger.info("Loaded GatewayFilterFactory [Authorize]");
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("enabled");
}
@Override
public GatewayFilter apply(AuthorizeGatewayFilterFactory.Config config) {
return (exchange, chain) -> {
if (!config.isEnabled()) {
return chain.filter(exchange);
}
ServerHttpRequest request = exchange.getRequest();
HttpHeaders headers = request.getHeaders();
String token = headers.getFirst(AUTHORIZE_TOKEN);
String uid = headers.getFirst(AUTHORIZE_UID);
if (token == null) {
token = request.getQueryParams().getFirst(AUTHORIZE_TOKEN);
}
if (uid == null) {
uid = request.getQueryParams().getFirst(AUTHORIZE_UID);
}
ServerHttpResponse response = exchange.getResponse();
if (StringUtils.isEmpty(token) || StringUtils.isEmpty(uid)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
String authToken = stringRedisTemplate.opsForValue().get(uid);
if (authToken == null || !authToken.equals(token)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
return chain.filter(exchange);
};
}
public static class Config {
// 控制是否開啟認證
private boolean enabled;
public Config() {}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
}
複製程式碼
在application.yml
配置使用
# 閘道器路由配置
spring:
cloud:
gateway:
routes:
- id: user-service
uri: http://localhost:8077/api/user/list
predicates:
- Path=/user/list
filters:
# 關鍵在下面一句,值為true則開啟認證,false則不開啟
# 這種配置方式和spring cloud gateway內建的GatewayFilterFactory一致
- Authorize=true
複製程式碼
自定義Global Filter
實現自定義全域性過濾器需要繼承GlobalFilter
和Ordered
@Component
public class MyGloablFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("1111111111111111");
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
複製程式碼
使用它只需要加上@Component
註解