Gateway的RemoteAddr与RemoteAddressResolver源码分析

1、RemoteAddr断言

1.1 源码解析

## RemoteAddrRoutePredicateFactorypublic 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());
        }
    };
}

## RemoteAddressResolverdefault InetSocketAddress resolve(ServerWebExchange exchange) {
	//获取直接连接网关的IP地址,如果通nginx转发到网关的,那么这里获取到的是nginx的IP地址。
    return exchange.getRequest().getRemoteAddress();
}

yml配置:

Gateway的RemoteAddr与RemoteAddressResolver源码分析_第1张图片

如果我们在yml的断言配置如下图,那么他将匹配不成功

Gateway的RemoteAddr与RemoteAddressResolver源码分析_第2张图片

2、XForwardedRemoteAddr断言

2.1 源码解析

## XForwardedRemoteAddrRoutePredicateFactorypublic 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;
    };
}

## RemoteAddrRoutePredicateFactorypublic 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;
        }
		...
    };
}

## XForwardedRemoteAddressResolverpublic 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);
    }
}

## XForwardedRemoteAddressResolverprivate 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。
Gateway的RemoteAddr与RemoteAddressResolver源码分析_第3张图片
如果我们在yml的断言配置如下图,那么他将匹配不成功。
Gateway的RemoteAddr与RemoteAddressResolver源码分析_第4张图片

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。

maxTrustedIndex值怎么设置没还有解决

你可能感兴趣的:(源码分析,gateway,java,spring)