1、前言
實際項目中存在這樣一個場景,使用Spring Cloud Gateway建設一個網關,該網關負責代理所有業務系統的對外通路請求。若外部服務需要授權(下文中的passID和passToken)才能通路,那麼該網關可以統一處理該問題。
以下引出添加AddRequestHeader的幾種方式,以及存在的限制。
2、使用yaml配置方式
采用yaml方式配置 ,通路csdn網站,X-Request-red = blue,該方式适用于已知資料的添加。
spring:
application:
name: gateway-application
cloud:
# Spring Cloud Gateway 配置項,對應 GatewayProperties 類
gateway:
# 路由配置項,對應 RouteDefinition 數組
routes:
- id: csdn1 # 路由的編号
uri: https://blog.csdn.net # 路由到的目标位址,隻能到域名部分 https://blog.csdn.net/dear_little_bear
predicates: # 斷言,作為路由的比對條件,對應 RouteDefinition 數組
- Path=/csdn1
filters:
- StripPrefix=1
- AddRequestHeader=X-Request-red, blue
3、使用RouteLocator程式設計方式
采用RouteLocator程式設計方式 ,通路csdn網站,該方式适用于不變的資料(“單例”)的添加。
@Configuration
public class RouteConfiguration {
@Value("${gateway.csdn.host}")
private String csdnHost;
@Value("${gateway.csdn.PaaSID}")
private String paaSID;
@Value("${gateway.csdn.PaaSToken}")
private String paaSToken;
@Value("${gateway.csdn.nonce}")
private String nonce;
@Autowired
private CsdnRequestFilter csdnRequestFilter;
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) throws UnsupportedEncodingException {
String timeTamp = String.valueOf(System.currentTimeMillis() / 1000);
String signature = SecureUtil.sha256(timeTamp + nonce + paaSToken);
return builder.routes()
.route(r -> r.path("/csdn2/**")
.filters(f ->
f.stripPrefix(1)
.addRequestHeader("x-tif-nonce", nonce)
.addRequestHeader("x-tif-signature", signature)
.addRequestHeader("x-tif-paasid", paaSID)
.addRequestHeader("x-tif-timestamp", timeTamp))
.uri(csdnHost)
.order(2))
/*.route(r -> r.path("/csdn3/**")
.filters(f->f.stripPrefix(1)
.filter(csdnRequestFilter))
.uri(csdnHost)
.order(3))*/
.build();
}
}
使用Postman請求:
兩次請求結果(不同時間點):
2020-04-04 14:16:15.141 DEBUG 10204 — [ctor-http-nio-3] o.s.c.g.h.RoutePredicateHandlerMapping : Mapping [Exchange: GET http://localhost:8888/csdn2] to Route{id=‘c3c865da-0fc2-47e3-aef2-55864afd626f’, uri=https://blog.csdn.net:443, order=2, predicate=Paths: [/csdn2/**], match trailing slash: true, gatewayFilters=[[[StripPrefix parts = 1], order = 0], [[AddRequestHeader x-tif-nonce = ‘123456789abcdefg’], order = 0], [[AddRequestHeader x-tif-signature = ‘3dee7471fe6b71a2b66f3b187c8cca844c3aef47afa4020f7df3b5c386015955’], order = 0], [[AddRequestHeader x-tif-paasid = ‘csdndemo’], order = 0], [[AddRequestHeader x-tif-timestamp = ‘1585980879’], order = 0]], metadata={}}
2020-04-04 14:20:14.343 DEBUG 10204 — [ctor-http-nio-3] o.s.c.g.h.RoutePredicateHandlerMapping : Mapping [Exchange: GET http://localhost:8888/csdn2] to Route{id=‘c3c865da-0fc2-47e3-aef2-55864afd626f’, uri=https://blog.csdn.net:443, order=2, predicate=Paths: [/csdn2/**], match trailing slash: true, gatewayFilters=[[[StripPrefix parts = 1], order = 0], [[AddRequestHeader x-tif-nonce = ‘123456789abcdefg’], order = 0], [[AddRequestHeader x-tif-signature = ‘3dee7471fe6b71a2b66f3b187c8cca844c3aef47afa4020f7df3b5c386015955’], order = 0], [[AddRequestHeader x-tif-paasid = ‘csdndemo’], order = 0], [[AddRequestHeader x-tif-timestamp = ‘1585980879’], order = 0]], metadata={}}
我們發現timestamp和signature都沒有發生變化,即便在customRouteLocator上加上Scope,依然無效。檢視源碼後發現,當我們啟動了Spring Cloud Gateway之後,該RouteLocator就會建立,并且不會重複new。但這不是我們想要的結果。
4、使用自定義gatewayfilter方式
(1) 自定義一個CsdnRequestFilter實作GatewayFilter,CsdnRequestFilter 代碼如下:
@Component
public class CsdnRequestFilter implements GatewayFilter, Ordered {
@Value("${gateway.csdn.PaaSID}")
private String paaSID;
@Value("${gateway.csdn.PaaSToken}")
private String paaSToken;
@Value("${gateway.csdn.nonce}")
private String nonce;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String timeTamp = String.valueOf(System.currentTimeMillis() / 1000);
String signature = SecureUtil.sha256(timeTamp + nonce + paaSToken);
System.out.print("自定義過濾器:timeTamp:"+timeTamp+";signature:"+signature);
//提取應用賬戶及應用令牌,鑒權
ServerHttpRequest request = exchange.getRequest().mutate()
.header("x-tif-nonce", nonce)
.header("x-tif-signature", signature)
.header("x-tif-paasid", paaSID)
.header("x-tif-timestamp", timeTamp)
.build();
return chain.filter(exchange.mutate().request(request).build());
}
@Override
public int getOrder() {
return 1;
}
}
(2) 在RouteLocator類中添加該gatewayfilter,代碼如下:
return builder.routes()
.route(r -> r.path("/csdn3/**")
.filters(f->f.stripPrefix(1)
.filter(csdnRequestFilter))
.uri(csdnHost)
.order(3))
.build();
兩次請求結果如下:
自定義過濾器:timeTamp:1585982693;signature:110ad2c949fb9e9afe2c8f98a81f6f445fd85ad43e0dec1ab555dae8f06b8f62 2020-04-04 14:44:53.180
自定義過濾器:timeTamp:1585980894;signature:265ff5cbac42c99969403285b897c24e2db59151f456a842669c7e7726fb9c23 2020-04-04 14:14:54.504
發現timeTamp和signature都發生了改變。原因是自定義gatewayfilter會在每次http請求過程中,動态建立。
特别注意
header是readonly類型,是以在CsdnRequestFilter中新增header,采用的是exchange.getRequest().mutate(),該方法将會構造出一個新的ServerHttpRequest。同時,也使用了exchange.mutate() 方法重新構造了一個ServerWebExchange 。
以上希望對你有幫助。
源碼: https://github.com/muziye2013/SpringBoot-Labs 參照 labx-08/labx-08-ex-gateway-demo01章節