From ef48563e4b021196487e4f35d4b52044512a47cc Mon Sep 17 00:00:00 2001
From: domainname <1830237+domainname@users.noreply.github.com>
Date: Thu, 22 Oct 2020 13:42:09 +0800
Subject: [PATCH 1/3] Add a WebServerFactoryCustomizer to customizes the
behavior of remote address resolver in spring-cloud-gateway sample. Also
upgrade the versions of Spring Boot and Spring Cloud.
---
gateway/pom.xml | 4 +-
.../gateway/NettyReactiveWebServerConfig.java | 56 +++++++++++++++++++
2 files changed, 58 insertions(+), 2 deletions(-)
create mode 100644 gateway/src/main/java/me/aboullaite/gateway/NettyReactiveWebServerConfig.java
diff --git a/gateway/pom.xml b/gateway/pom.xml
index e58681e..26aa8a2 100644
--- a/gateway/pom.xml
+++ b/gateway/pom.xml
@@ -5,7 +5,7 @@
org.springframework.boot
spring-boot-starter-parent
- 2.1.8.RELEASE
+ 2.2.10.RELEASE
me.aboullaite
@@ -16,7 +16,7 @@
1.8
- Greenwich.RELEASE
+ Hoxton.SR8
diff --git a/gateway/src/main/java/me/aboullaite/gateway/NettyReactiveWebServerConfig.java b/gateway/src/main/java/me/aboullaite/gateway/NettyReactiveWebServerConfig.java
new file mode 100644
index 0000000..c08a7a7
--- /dev/null
+++ b/gateway/src/main/java/me/aboullaite/gateway/NettyReactiveWebServerConfig.java
@@ -0,0 +1,56 @@
+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 {
+
+ @Override
+ public void customize(NettyReactiveWebServerFactory factory) {
+ factory.addServerCustomizers(httpServer -> httpServer.forwarded(new CustomizedHttpForwardedHeaderHandler()));
+ }
+
+ static class CustomizedHttpForwardedHeaderHandler
+ implements BiFunction {
+
+ 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(",");
+ InetSocketAddress remoteAddress = InetSocketAddressUtil.parseAddress(ips[ips.length - 1].trim(),
+ connectionInfo.getRemoteAddress().getPort());
+ connectionInfo = connectionInfo.withRemoteAddress(remoteAddress);
+ }
+
+ return connectionInfo;
+ }
+ }
+}
From 0cba83fbd72278d086826becb37e6c48b79adb04 Mon Sep 17 00:00:00 2001
From: domainname <1830237+domainname@users.noreply.github.com>
Date: Fri, 23 Oct 2020 13:46:56 +0800
Subject: [PATCH 2/3] Add a controller to demonstrate the customized behavior
of ServerHttpRequest#getRemoteAddress
---
.../controller/RemoteAddressController.java | 27 +++++++++++++++++++
1 file changed, 27 insertions(+)
create mode 100644 gateway/src/main/java/me/aboullaite/gateway/controller/RemoteAddressController.java
diff --git a/gateway/src/main/java/me/aboullaite/gateway/controller/RemoteAddressController.java b/gateway/src/main/java/me/aboullaite/gateway/controller/RemoteAddressController.java
new file mode 100644
index 0000000..f61e8f5
--- /dev/null
+++ b/gateway/src/main/java/me/aboullaite/gateway/controller/RemoteAddressController.java
@@ -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()
+ );
+ }
+}
From 2a77ebd5db52d4604f69e64b80bb6fbf4ffda028 Mon Sep 17 00:00:00 2001
From: domainname <1830237+domainname@users.noreply.github.com>
Date: Tue, 27 Oct 2020 12:42:56 +0800
Subject: [PATCH 3/3] In CustomizedHttpForwardedHeaderHandler, we skip internal
IP address in X-Forwarded-For and extract the previous one.
---
.../gateway/NettyReactiveWebServerConfig.java | 13 ++++++++++++-
.../gateway/controller/RemoteAddressController.java | 2 +-
2 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/gateway/src/main/java/me/aboullaite/gateway/NettyReactiveWebServerConfig.java b/gateway/src/main/java/me/aboullaite/gateway/NettyReactiveWebServerConfig.java
index c08a7a7..c0750c7 100644
--- a/gateway/src/main/java/me/aboullaite/gateway/NettyReactiveWebServerConfig.java
+++ b/gateway/src/main/java/me/aboullaite/gateway/NettyReactiveWebServerConfig.java
@@ -45,12 +45,23 @@ public ConnectionInfo apply(ConnectionInfo connectionInfo, HttpRequest request)
if (ipHeader != null) {
String[] ips = ipHeader.split(",");
- InetSocketAddress remoteAddress = InetSocketAddressUtil.parseAddress(ips[ips.length - 1].trim(),
+ 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.");
+ }
}
}
diff --git a/gateway/src/main/java/me/aboullaite/gateway/controller/RemoteAddressController.java b/gateway/src/main/java/me/aboullaite/gateway/controller/RemoteAddressController.java
index f61e8f5..45d34a2 100644
--- a/gateway/src/main/java/me/aboullaite/gateway/controller/RemoteAddressController.java
+++ b/gateway/src/main/java/me/aboullaite/gateway/controller/RemoteAddressController.java
@@ -19,7 +19,7 @@ public class RemoteAddressController {
@GetMapping("/remote_address")
public String getRemoteAddress(ServerHttpRequest request) {
return String.format("X-Forwarded-For: %s\n" +
- "getRemoteAddress returns %s",
+ "getRemoteAddress() returns %s",
request.getHeaders().get("X-Forwarded-For"),
request.getRemoteAddress()
);