域名解析
从物理机上调用外部服务正常,但是docker里的java服务去调用却有问题。
答案
docker并不能使用宿主机的host配置信息
为每一个http请求定制header
如果在RouteLocatorBuilder里设置header的话就会对所有http request生效,如果为了对每个request请求使用不同header需要如下设置
@Configuration
public class GatewayConfiguration {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder, Client client){
return builder.routes()
.route("qbit",
request->request
.alwaysTrue()
.filters(
f->f.filter(client)
).uri("http://"+client.getAddress()+"/")).build();
}
}
@Slf4j
@Component
public class Client implements GatewayFilter {
private final FastDateFormat fastDateFormat=FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss");
public String getTimestamp() {
return fastDateFormat.format(new Date());
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
final var timestamp=this.getTimestamp();
String contentType = "application/json";
ServerHttpRequest request = exchange.getRequest().mutate()
.header("Content-Type", contentType)
.header("Timestamp",timestamp)
.build();
log.info("curl -X "+request.getMethod()+" "+
Joiner.on(' ').join(
request.getHeaders().entrySet().stream().map(e->"--header '"+e.getKey()+":"+e.getValue().get(0)+"'").collect(Collectors.toList())
)+" -d '?' '"+ StringUtils.replace(request.getURI().toString(),"spring-gateway:8080",getAddress())+"'");
return chain.filter(exchange.mutate().request(request).build());
}
关键就是implements GatewayFilter
上面log只是随便写写,根本没有tcpdump打印的全,但看可以做到每个请求的header里Timestamp的值是不一样的。
白名单
有一个外部服务,对调用方的ip地址做了白名单,于是我们这边的架构就是:把Gateway部署在白名单所在的服务器上,然后业务Service调用Gateway,完成token等安全信息,然后Gateway转发到外部,谁知道却报ip不合法。但是从docker内部使用curl来调用却正常。
答案
Gateway会像nginx那样加上一些Forward信息,这样对方就会读出业务服务的地址(当然不在白名单,并且是docker内部的网络地址)
诊断过程
使用下面命令监听IP报文
tcpdump -vvv -i eno16 -n dst host api.qbit.cn
其中eno16是为了选中网卡,vvv是为了打印出全部信息,可以看到HTTP信息如下
POST /services/qbit HTTP/1.1
User-Agent: curl/7.64.0
Accept: */*
Content-Length: 45
Content-Type: application/json
Host: spring-gateway
Forwarded: proto=http;host=api.qbit.cn;for="10.42.0.100:53868"
X-Forwarded-For: 10.42.0.100
X-Forwarded-Proto: http
X-Forwarded-Port: 8080
X-Forwarded-Host: spring-gateway:8080
可以看到Host需要在Gateway里替换成外部服务地址(否则可能404),然后X-Forwarded这些head需要去掉,否则对方会从这里面读取到10.42.0.100这个docker地址
解决方案
spring:
cloud:
gateway:
x-forwarded:
enabled: false
关于这个功能的更详细用法可以参考https://cloud.tencent.com/developer/article/1403887