Skip to content
This repository was archived by the owner on Jun 17, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions gateway/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<version>2.2.10.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>me.aboullaite</groupId>
Expand All @@ -16,7 +16,7 @@

<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
</properties>

<dependencies>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package me.aboullaite.gateway;

import io.netty.handler.codec.http.HttpRequest;
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.reactive.ServerHttpRequest;
import reactor.netty.http.server.ConnectionInfo;
import reactor.netty.tcp.InetSocketAddressUtil;

import java.net.InetSocketAddress;
import java.util.function.BiFunction;

/**
* This configuration customizes the behavior of remote address resolver. As a result,
* {@link ServerHttpRequest#getRemoteAddress} will return IP address that is extracted from X-Forwarded-For header
* by our {@link CustomizedHttpForwardedHeaderHandler}.
*/
@Configuration
public class NettyReactiveWebServerConfig implements WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {

@Override
public void customize(NettyReactiveWebServerFactory factory) {
factory.addServerCustomizers(httpServer -> httpServer.forwarded(new CustomizedHttpForwardedHeaderHandler()));
}

static class CustomizedHttpForwardedHeaderHandler
implements BiFunction<ConnectionInfo, HttpRequest, ConnectionInfo> {

static final String X_FORWARDED_IP_HEADER = "X-Forwarded-For";

/**
* Attempt to extract the last IP address from X-Forwarded-For header. If the header is present, create a
* ConnectionInfo instance with the extracted IP address and then return it. Otherwise, return the
* connectionInfo passed in. On the contrary, DefaultHttpForwardedHeaderHandler extracts the first IP
* address from the header when the header is present.
*
* @param connectionInfo
* @param request
* @return
*/
@Override
public ConnectionInfo apply(ConnectionInfo connectionInfo, HttpRequest request) {
String ipHeader = request.headers().get(X_FORWARDED_IP_HEADER);

if (ipHeader != null) {
String[] ips = ipHeader.split(",");
String ip = ips[ips.length - 1].trim();

// If the IP address is internal, we will skip it and extract the previous one.
if (ips.length >= 2 && isInternal(ip)) {
ip = ips[ips.length - 2].trim();
}

InetSocketAddress remoteAddress = InetSocketAddressUtil.parseAddress(ip,
connectionInfo.getRemoteAddress().getPort());
connectionInfo = connectionInfo.withRemoteAddress(remoteAddress);
}

return connectionInfo;
}

private boolean isInternal(String ip) {
return ip.startsWith("10.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package me.aboullaite.gateway.controller;

import me.aboullaite.gateway.NettyReactiveWebServerConfig;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* A demo controller that shows the customized behavior of {@link ServerHttpRequest#getRemoteAddress}. The
* customization class is CustomizedHttpForwardedHeaderHandler in {@link NettyReactiveWebServerConfig}.
*/
@RestController
public class RemoteAddressController {

/**
* @param request
* @return Value of X-Forwarded-For header, as well as result of {@link ServerHttpRequest#getRemoteAddress}
*/
@GetMapping("/remote_address")
public String getRemoteAddress(ServerHttpRequest request) {
return String.format("X-Forwarded-For: %s\n" +
"getRemoteAddress() returns %s",
request.getHeaders().get("X-Forwarded-For"),
request.getRemoteAddress()
);
}
}