本文主要研究一下spring cloud gateway的ForwardedHeadersFilterhtml
spring-cloud-gateway-core-2.0.0.RC1-sources.jar!/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.javajava
@Configuration @ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true) @EnableConfigurationProperties @AutoConfigureBefore(HttpHandlerAutoConfiguration.class) @AutoConfigureAfter({GatewayLoadBalancerClientAutoConfiguration.class, GatewayClassPathWarningAutoConfiguration.class}) @ConditionalOnClass(DispatcherHandler.class) public class GatewayAutoConfiguration { //...... @Bean @ConditionalOnProperty(name = "spring.cloud.gateway.forwarded.enabled", matchIfMissing = true) public ForwardedHeadersFilter forwardedHeadersFilter() { return new ForwardedHeadersFilter(); } //...... }
spring-cloud-gateway-core-2.0.0.RC1-sources.jar!/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilter.javanginx
public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { public static final String FORWARDED_HEADER = "Forwarded"; @Override public int getOrder() { return 0; } @Override public HttpHeaders filter(HttpHeaders input, ServerWebExchange exchange) { ServerHttpRequest request = exchange.getRequest(); HttpHeaders original = input; HttpHeaders updated = new HttpHeaders(); // copy all headers except Forwarded original.entrySet().stream() .filter(entry -> !entry.getKey().toLowerCase().equalsIgnoreCase(FORWARDED_HEADER)) .forEach(entry -> updated.addAll(entry.getKey(), entry.getValue())); List<Forwarded> forwardeds = parse(original.get(FORWARDED_HEADER)); for (Forwarded f : forwardeds) { updated.add(FORWARDED_HEADER, f.toString()); } //TODO: add new forwarded URI uri = request.getURI(); String host = original.getFirst(HttpHeaders.HOST); Forwarded forwarded = new Forwarded() .put("host", host) .put("proto", uri.getScheme()); InetSocketAddress remoteAddress = request.getRemoteAddress(); if (remoteAddress != null) { String forValue = remoteAddress.getAddress().getHostAddress(); int port = remoteAddress.getPort(); if (port >= 0) { forValue = forValue + ":" + port; } forwarded.put("for", forValue); } // TODO: support by? updated.add(FORWARDED_HEADER, forwarded.toHeaderValue()); return updated; } /* for testing */ static List<Forwarded> parse(List<String> values) { ArrayList<Forwarded> forwardeds = new ArrayList<>(); if (CollectionUtils.isEmpty(values)) { return forwardeds; } for (String value : values) { Forwarded forwarded = parse(value); forwardeds.add(forwarded); } return forwardeds; } /* for testing */ static Forwarded parse(String value) { String[] pairs = StringUtils.tokenizeToStringArray(value, ";"); LinkedCaseInsensitiveMap<String> result = splitIntoCaseInsensitiveMap(pairs); if (result == null) return null; Forwarded forwarded = new Forwarded(result); return forwarded; } @Nullable /* for testing */ static LinkedCaseInsensitiveMap<String> splitIntoCaseInsensitiveMap(String[] pairs) { if (ObjectUtils.isEmpty(pairs)) { return null; } LinkedCaseInsensitiveMap<String> result = new LinkedCaseInsensitiveMap<>(); for (String element : pairs) { String[] splittedElement = StringUtils.split(element, "="); if (splittedElement == null) { continue; } result.put(splittedElement[0].trim(), splittedElement[1].trim()); } return result; } }
這個filter首先拷貝了請求的header,而後將請求中的Forwarded提取出來,解析成一個個Forwarded對象,添加到新的HttpHeaders中。除此以外,還補充了一個轉發信息的Forwarded(
host,proto,for
)
Forwarded: by=<identifier>; for=<identifier>; host=<host>; proto=<http|https>
Forwarded: for=192.0.2.60; proto=http; by=203.0.113.43 Forwarded: proto=http;host="localhost:10000";for="0:0:0:0:0:0:0:1:56443"
static class Forwarded { private static final char EQUALS = '='; private static final char SEMICOLON = ';'; private final Map<String, String> values; public Forwarded() { this.values = new HashMap<>(); } public Forwarded(Map<String, String> values) { this.values = values; } public Forwarded put(String key, String value) { this.values.put(key, quoteIfNeeded(value)); return this; } private String quoteIfNeeded(String s) { if (s.contains(":")) { //TODO: broaded quote return "\""+s+"\""; } return s; } public String get(String key) { return this.values.get(key); } /* for testing */ Map<String, String> getValues() { return this.values; } @Override public String toString() { return "Forwarded{" + "values=" + this.values + '}'; } public String toHeaderValue() { StringBuilder builder = new StringBuilder(); for (Map.Entry<String, String> entry : this.values.entrySet()) { if (builder.length() > 0) { builder.append(SEMICOLON); } builder.append(entry.getKey()) .append(EQUALS) .append(entry.getValue()); } return builder.toString(); } }
RFC 7239(June 2014
)提出了一個標準化的Forwarded頭部,來攜帶反向代理的基本信息,用於替代X-Forwarded系列及X-Real-IP等非標準化的頭部。而ForwardedHeadersFilter即是提供了Forwarded頭部的轉發支持,目前通過gateway的請求會帶上一個轉發信息的Forwarded(host,proto,for
)。spring