## RemoteAddrRoutePredicateFactory类
public Predicate<ServerWebExchange> apply(RemoteAddrRoutePredicateFactory.Config config) {
//获取yml配置的地址信息,如下图获取的到地址是192.168.21.21
final List<IpSubnetFilterRule> sources = this.convert(config.sources);
return new GatewayPredicate() {
public boolean test(ServerWebExchange exchange) {
//获取请求的地址(执行的代码就是下面那一个方法) 如下图获取到的是192.168.21.100
InetSocketAddress remoteAddress = config.remoteAddressResolver.resolve(exchange);
if (remoteAddress != null && remoteAddress.getAddress() != null) {
String hostAddress = remoteAddress.getAddress().getHostAddress();
String host = exchange.getRequest().getURI().getHost();
if (RemoteAddrRoutePredicateFactory.log.isDebugEnabled() && !hostAddress.equals(host)) {
RemoteAddrRoutePredicateFactory.log.debug("Remote addresses didn't match " + hostAddress + " != " + host);
}
Iterator var5 = sources.iterator();
while(var5.hasNext()) {
IpSubnetFilterRule source = (IpSubnetFilterRule)var5.next();
//通过正则匹配判断是否通过
if (source.matches(remoteAddress)) {
return true;
}
}
}
return false;
}
public Object getConfig() {
return config;
}
public String toString() {
return String.format("RemoteAddrs: %s", config.getSources());
}
};
}
## RemoteAddressResolver类
default InetSocketAddress resolve(ServerWebExchange exchange) {
//获取直接连接网关的IP地址,如果通nginx转发到网关的,那么这里获取到的是nginx的IP地址。
return exchange.getRequest().getRemoteAddress();
}
yml配置:
如果我们在yml的断言配置如下图,那么他将匹配不成功
## XForwardedRemoteAddrRoutePredicateFactory类
public Predicate<ServerWebExchange> apply(XForwardedRemoteAddrRoutePredicateFactory.Config config) {
if (log.isDebugEnabled()) {
log.debug("Applying XForwardedRemoteAddr route predicate with maxTrustedIndex of " + config.getMaxTrustedIndex() + " for " + config.getSources().size() + " source(s)");
}
org.springframework.cloud.gateway.handler.predicate.RemoteAddrRoutePredicateFactory.Config wrappedConfig = new org.springframework.cloud.gateway.handler.predicate.RemoteAddrRoutePredicateFactory.Config();
/**
* 获取yml配置的IP地址信息,将从获取yml配置的IP地址信息传递
* 给RemoteAddrRoutePredicateFactory,如下图获取的到地址是192.168.21.21
*/
wrappedConfig.setSources(config.getSources());
/**
* config.getMaxTrustedIndex() 默认值为 1
* XForwardedRemoteAddressResolver重新了resolve这个方法,用来获取请求头中X-Forwarded-For的值
*/
wrappedConfig.setRemoteAddressResolver(XForwardedRemoteAddressResolver.maxTrustedIndex(config.getMaxTrustedIndex()));
//说明RemoteAddrRoutePredicateFactory底层还是用RemoteAddr断言的规则
RemoteAddrRoutePredicateFactory remoteAddrRoutePredicateFactory = new RemoteAddrRoutePredicateFactory();
//将封装好的config传递给RemoteAddrRoutePredicateFactory
Predicate<ServerWebExchange> wrappedPredicate = remoteAddrRoutePredicateFactory.apply(wrappedConfig);
return (exchange) -> {
Boolean isAllowed = wrappedPredicate.test(exchange);
if (log.isDebugEnabled()) {
ServerHttpRequest request = exchange.getRequest();
log.debug("Request for \"" + request.getURI() + "\" from client \"" + request.getRemoteAddress().getAddress().getHostAddress() + "\" with \"" + "X-Forwarded-For" + "\" header value of \"" + request.getHeaders().get("X-Forwarded-For") + "\" is " + (isAllowed ? "ALLOWED" : "NOT ALLOWED"));
}
return isAllowed;
};
}
## RemoteAddrRoutePredicateFactory类
public Predicate<ServerWebExchange> apply(RemoteAddrRoutePredicateFactory.Config config) {
//获取传递过来的IP地址,如下图获取的到地址是192.168.21.21
final List<IpSubnetFilterRule> sources = this.convert(config.sources);
return new GatewayPredicate() {
public boolean test(ServerWebExchange exchange) {
//获取请求的地址(执行的代码就是下面那一个方法) 如下图获取到的是192.168.21.22
InetSocketAddress remoteAddress = config.remoteAddressResolver.resolve(exchange);
if (remoteAddress != null && remoteAddress.getAddress() != null) {
String hostAddress = remoteAddress.getAddress().getHostAddress();
String host = exchange.getRequest().getURI().getHost();
if (RemoteAddrRoutePredicateFactory.log.isDebugEnabled() && !hostAddress.equals(host)) {
RemoteAddrRoutePredicateFactory.log.debug("Remote addresses didn't match " + hostAddress + " != " + host);
}
Iterator var5 = sources.iterator();
while(var5.hasNext()) {
IpSubnetFilterRule source = (IpSubnetFilterRule)var5.next();
//通过正则匹配判断是否通过
if (source.matches(remoteAddress)) {
return true;
}
}
}
return false;
}
...
};
}
## XForwardedRemoteAddressResolver类
public InetSocketAddress resolve(ServerWebExchange exchange) {
//获取请求头中X-Forwarded-For的值
List<String> xForwardedValues = this.extractXForwardedValues(exchange);
Collections.reverse(xForwardedValues);
if (!xForwardedValues.isEmpty()) {
//获取指定位置的IP,
int index = Math.min(xForwardedValues.size(), this.maxTrustedIndex) - 1;
return new InetSocketAddress((String)xForwardedValues.get(index), 0);
} else {
return this.defaultRemoteIpResolver.resolve(exchange);
}
}
## XForwardedRemoteAddressResolver类
private List<String> extractXForwardedValues(ServerWebExchange exchange) {
List<String> xForwardedValues = exchange.getRequest().getHeaders().get("X-Forwarded-For");
if (xForwardedValues != null && !xForwardedValues.isEmpty()) {
//如果X-Forwarded-For的值多个直接丢弃
if (xForwardedValues.size() > 1) {
log.warn("Multiple X-Forwarded-For headers found, discarding all");
return Collections.emptyList();
} else {
List<String> values = Arrays.asList(((String)xForwardedValues.get(0)).split(", "));
return values.size() == 1 && !StringUtils.hasText((String)values.get(0)) ? Collections.emptyList() : values;
}
} else {
return Collections.emptyList();
}
}
因config.getMaxTrustedIndex()
的默认值为1,所以请求头X-Forwarded-For
值的最后一个值,如下图,获取的值为192.168.21.22。
如果我们在yml的断言配置如下图,那么他将匹配不成功。
XForwardedRemoteAddressResolver有两个静态构造函数方法,它们采用不同的安全方法:
XForwardedRemoteAddressResolver::trustAllRemoteAddressResolver返回始终采用标头中找到的第一个 IP 地址的a X-Forwarded-For。这种方法容易受到欺骗,因为恶意客户端可以为 设置一个初始值X-Forwarded-For,解析器会接受该值。
XForwardedRemoteAddressResolver::maxTrustedIndex采用与在 Spring Cloud Gateway 前面运行的可信基础设施数量相关的索引。例如,如果 Spring Cloud Gateway 只能通过 HAProxy 访问,则应使用值 1。如果在访问 Spring Cloud Gateway 之前需要两跳可信基础设施,则应使用值 2。