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() );