域名解析

从物理机上调用外部服务正常,但是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

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐