How to get remote address from request of Netty HTTP Server

本文详细介绍了在Netty HTTP服务器中如何通过HTTP请求获取远程地址,包括使用Forwarded和X-Forwarded-For头部信息的逻辑。同时,讲解了如何通过配置文件或NettyWebServerFactoryCustomizer启用use-forward-headers来正确获取客户端的真实IP,以及在Nginx反向代理环境下如何设置X-Forwarded-For头部。

获取 remoteAddress 的逻辑

通过HTTP请求获取 remote address 时,最终是调用 ConnectionInfo 的 getRemoteAddress() 方法。

reactor.netty.http.server.HttpServerOperations#remoteAddress

@Override
public InetSocketAddress remoteAddress() {
	return this.connectionInfo.getRemoteAddress();
}

ConnectionInfo 实例的创建过程如下:

从 “Forwarded” 或 "X-Forwarded-" HTTP请求头中获取连接信息,如果不存在则直接从当前连接获取连接信息。*

请注意:如果 Nginx 和 Netty HTTP Server 位于同一服务器内,且未启动 use-forward-headers,则获取的 remoteAddress 为 127.0.0.1 。

reactor.netty.http.server.ConnectionInfo#from

static ConnectionInfo from(Channel channel, boolean headers, HttpRequest request, boolean secured) {
	if (headers) {
		return ConnectionInfo.newForwardedConnectionInfo(request, channel, secured);
	}
	else {
		return ConnectionInfo.newConnectionInfo(channel, secured);
	}
}

static ConnectionInfo newForwardedConnectionInfo(HttpRequest request, Channel channel, boolean secured) {
	if (request.headers().contains(FORWARDED_HEADER)) {
		return parseForwardedInfo(request, (SocketChannel)channel, secured);
	}
	else {
		return parseXForwardedInfo(request, (SocketChannel)channel, secured);
	}
}

static ConnectionInfo parseXForwardedInfo(HttpRequest request, SocketChannel channel, boolean secured) {
	InetSocketAddress hostAddress = channel.localAddress();
	InetSocketAddress remoteAddress = getRemoteAddress(channel);
	String scheme =  secured ? "https" : "http";
	if (request.headers().contains(XFORWARDED_IP_HEADER)) {
		String remoteIpValue = request.headers().get(XFORWARDED_IP_HEADER).split(",")[0];
		remoteAddress = parseAddress(remoteIpValue, remoteAddress.getPort());
	}
	if(request.headers().contains(XFORWARDED_HOST_HEADER)) {
		if(request.headers().contains(XFORWARDED_PORT_HEADER)) {
			hostAddress = InetSocketAddressUtil.createUnresolved(
					request.headers().get(XFORWARDED_HOST_HEADER).split(",")[0].trim(),
					Integer.parseInt(request.headers().get(XFORWARDED_PORT_HEADER).split(",")[0].trim()));
		}
		else {
			hostAddress = InetSocketAddressUtil.createUnresolved(
					request.headers().get(XFORWARDED_HOST_HEADER).split(",")[0].trim(),
					channel.localAddress().getPort());
		}
	}
	if (request.headers().contains(XFORWARDED_PROTO_HEADER)) {
		scheme = request.headers().get(XFORWARDED_PROTO_HEADER).trim();
	}
	return new ConnectionInfo(hostAddress, remoteAddress, scheme);
}

通过配置文件启用 use-forward-headers

要想通过 request header 获取连接信息,需要配置 use-forward-headers = true。(默认为 false)

server:
  port: 9090
  use-forward-headers: true

通过 NettyWebServerFactoryCustomizer 配置

也可以通过NettyWebServerFactoryCustomizer启动,示例代码如下:

@Configuration
public class NettyWebServerConfiguration {
    @Bean
    public NettyWebServerFactoryCustomizer nettyServerWiretapCustomizer(
            Environment environment, ServerProperties serverProperties) {
        return new NettyWebServerFactoryCustomizer(environment, serverProperties) {
            @Override
            public void customize(NettyReactiveWebServerFactory factory) {
                factory.addServerCustomizers(httpServer -> httpServer.forwarded(true));
                super.customize(factory);
            }
        };
    }
}

set X-Forwarded-For header from nginx

1.通过“proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for” 把从真实客户端IP和反向代理IP通过逗号分隔,添加到请求头中;
2.可以在第一个反向代理上配置“proxy_set_header X-Real-IP $remote_addr” 获取真实客户端IP;
3.配合 realip 模块从 X-Forwarded-For 也可以获取到真实客户端IP。

在整个反向代理链条的第一个反向代理可以不配置“proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for”,否则客户端可以伪造 X-Forwarded-For 从而伪造客户端真实 IP,如果服务端使用 X-Forwarded-For 第一个IP作为真实客户端 IP,则就出问题了。如果通过配置 X-Real-IP 请求头或者配合 realip 模块也不会出现该问题。如果自己解析 X-Forwarded-For 的话,记得使用 realip 算法解析,而不是取第一个。
当我们进行限流时一定注意限制的是真实客户端IP,而不是反向代理IP,我遇到过很多同事在这块出问题的。

location / {
    proxy_next_upstream		http_500 http_502 http_503 http_504 error timeout invalid_header;
    proxy_set_header		Host  $host;
    proxy_set_header		X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass				http://tomcat_microservice;
    expires					0;
}

参考

反向代理与 Real-IP 和 X-Forwarded-For

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值