diff --git a/pom.xml b/pom.xml
index ae929191..3315ac3b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -81,6 +81,11 @@
org.springframework.boot
spring-boot-devtools
+
+ commons-net
+ commons-net
+ 3.5
+
org.springframework.cloud
spring-cloud-starter-eureka
diff --git a/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java b/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java
index a5d51583..f5d012f4 100644
--- a/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java
+++ b/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java
@@ -40,6 +40,7 @@ import org.springframework.cloud.gateway.handler.predicate.HeaderRoutePredicate;
import org.springframework.cloud.gateway.handler.predicate.HostRoutePredicate;
import org.springframework.cloud.gateway.handler.predicate.MethodRoutePredicate;
import org.springframework.cloud.gateway.handler.predicate.QueryRoutePredicate;
+import org.springframework.cloud.gateway.handler.predicate.RemoteAddrRoutePredicate;
import org.springframework.cloud.gateway.handler.predicate.RoutePredicate;
import org.springframework.cloud.gateway.handler.predicate.UrlRoutePredicate;
import org.springframework.context.annotation.Bean;
@@ -163,6 +164,12 @@ public class GatewayAutoConfiguration {
return new QueryRoutePredicate();
}
+ @Bean(name = "RemoteAddrRoutePredicate")
+ public RemoteAddrRoutePredicate remoteAddrRoutePredicate() {
+ return new RemoteAddrRoutePredicate();
+ }
+
+
@Bean(name = "UrlRoutePredicate")
public UrlRoutePredicate urlRoutePredicate() {
return new UrlRoutePredicate();
diff --git a/src/main/java/org/springframework/cloud/gateway/handler/predicate/RemoteAddrRoutePredicate.java b/src/main/java/org/springframework/cloud/gateway/handler/predicate/RemoteAddrRoutePredicate.java
new file mode 100644
index 00000000..1219c1f8
--- /dev/null
+++ b/src/main/java/org/springframework/cloud/gateway/handler/predicate/RemoteAddrRoutePredicate.java
@@ -0,0 +1,66 @@
+package org.springframework.cloud.gateway.handler.predicate;
+
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.net.util.SubnetUtils;
+import org.springframework.http.server.reactive.ReactorServerHttpRequest;
+import org.springframework.web.server.ServerWebExchange;
+
+/**
+ * @author Spencer Gibb
+ */
+public class RemoteAddrRoutePredicate implements RoutePredicate {
+
+ private static final Log log = LogFactory.getLog(RemoteAddrRoutePredicate.class);
+
+ @Override
+ public Predicate apply(String source, String[] args) {
+ List sources = new ArrayList<>();
+ addSource(sources, source);
+
+ if (args != null) {
+ for (String arg : args) {
+ addSource(sources, arg);
+ }
+ }
+
+ return exchange -> {
+ ReactorServerHttpRequest request = (ReactorServerHttpRequest) exchange.getRequest();
+ InetSocketAddress remoteAddress = request.getReactorRequest().remoteAddress();
+ String hostAddress = remoteAddress.getAddress().getHostAddress();
+ String host = exchange.getRequest().getURI().getHost();
+
+ if (!hostAddress.equals(host)) {
+ log.warn("Remote addresses didn't match " + hostAddress + " != " + host);
+ }
+
+ for (SubnetUtils subnet : sources) {
+ if (subnet.getInfo().isInRange(hostAddress)) {
+ return true;
+ }
+ }
+
+ return false;
+ };
+ }
+
+ private void addSource(List sources, String source) {
+ boolean inclusiveHostCount = false;
+ if (!source.contains("/")) { // no netmask, add default
+ source = source + "/32";
+ }
+ if (source.endsWith("/32")) {
+ //http://stackoverflow.com/questions/2942299/converting-cidr-address-to-subnet-mask-and-network-address#answer-6858429
+ inclusiveHostCount = true;
+ }
+ //TODO: howto support ipv6 as well?
+ SubnetUtils subnetUtils = new SubnetUtils(source);
+ subnetUtils.setInclusiveHostCount(inclusiveHostCount);
+ sources.add(subnetUtils);
+ }
+}
diff --git a/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java b/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java
index 5645289a..089c067e 100644
--- a/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java
+++ b/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java
@@ -43,7 +43,11 @@ public class GatewayIntegrationTests {
private static final String HANDLER_MAPPER_HEADER = "X-Gateway-Handler-Mapper-Class";
private static final String ROUTE_ID_HEADER = "X-Gateway-Route-Id";
- public static final Duration DURATION = Duration.ofSeconds(5);
+ private static final Duration DURATION = Duration.ofSeconds(5);
+
+ static {
+ System.setProperty("java.net.preferIPv4Stack", "true");
+ }
@LocalServerPort
private int port;
diff --git a/src/test/java/org/springframework/cloud/gateway/test/GatewayTestApplication.java b/src/test/java/org/springframework/cloud/gateway/test/GatewayTestApplication.java
index 012d919a..88997176 100644
--- a/src/test/java/org/springframework/cloud/gateway/test/GatewayTestApplication.java
+++ b/src/test/java/org/springframework/cloud/gateway/test/GatewayTestApplication.java
@@ -53,6 +53,7 @@ public class GatewayTestApplication {
}
public static void main(String[] args) {
+ System.setProperty("java.net.preferIPv4Stack", "true"); //Remove when configurable
SpringApplication.run(GatewayTestApplication.class, args);
}
}
diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml
index 8f05ad0e..22db75bc 100644
--- a/src/test/resources/application.yml
+++ b/src/test/resources/application.yml
@@ -140,6 +140,7 @@ spring:
uri: http://httpbin.org:80
predicates:
- Host=**.baz.org
+ - RemoteAddr=127.0.0.1/24
filters:
# $\ is being used as an escape
- RewritePath=/foo/(?.*), /$\{segment}