-
HTTPS无法正确获取
-
代理端口变化后无法正确获取
-
url截取后无法正确获取uri
1. 相关资料
X-Forwarded-For (XFF)::
X-Forwarded-For: <client>, <proxy1>, <proxy2>
- X-Forwarded-Port
X-Forwarded-Port: <port>
2. 问题原因解析
org/springframework/cloud/netflix/zuul/filters/pre/PreDecorationFilter.java
private void addProxyHeaders(RequestContext ctx, Route route) {
HttpServletRequest request = ctx.getRequest();
String host = toHostHeader(request);
String port = String.valueOf(request.getServerPort());
String proto = request.getScheme();
if (hasHeader(request, "X-Forwarded-Host")) {
host = request.getHeader("X-Forwarded-Host") + "," + host;
if (!hasHeader(request, "X-Forwarded-Port")) {
if (hasHeader(request, "X-Forwarded-Proto")) {
StringBuilder builder = new StringBuilder();
for (String previous : StringUtils.commaDelimitedListToStringArray(request.getHeader("X-Forwarded-Proto"))) {
if (builder.length()>0) {
builder.append(",");
}
builder.append("https".equals(previous) ? "443" : "80");
}
builder.append(",").append(port);
port = builder.toString();
}
} else {
port = request.getHeader("X-Forwarded-Port") + "," + port;
}
proto = request.getHeader("X-Forwarded-Proto") + "," + proto;
}
ctx.addZuulRequestHeader("X-Forwarded-Host", host);
ctx.addZuulRequestHeader("X-Forwarded-Port", port);
ctx.addZuulRequestHeader(ZuulHeaders.X_FORWARDED_PROTO, proto);
addProxyPrefix(ctx, route);
}
org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer.java类中定制:
org.apache.catalina.valves.RemoteIpValve.java
public void invoke(Request request, Response response) throws IOException, ServletException {
//...
if (protocolHeader != null) {
String protocolHeaderValue = request.getHeader(protocolHeader);
if (protocolHeaderValue == null) {
// Don't modify the secure, scheme and serverPort attributes
// of the request
} else if (isForwardedProtoHeaderValueSecure(protocolHeaderValue)) {
request.setSecure(true);
request.getCoyoteRequest().scheme().setString("https");
setPorts(request, httpsServerPort);
} else {
request.setSecure(false);
request.getCoyoteRequest().scheme().setString("http");
setPorts(request, httpServerPort);
}
}
//...
}
private boolean isForwardedProtoHeaderValueSecure(String protocolHeaderValue) {
if (!protocolHeaderValue.contains(",")) {
return protocolHeaderHttpsValue.equalsIgnoreCase(protocolHeaderValue);
}
String[] forwardedProtocols = commaDelimitedListToStringArray(protocolHeaderValue);
if (forwardedProtocols.length == 0) {
return false;
}
for (int i = 0; i < forwardedProtocols.length; i++) {
if (!protocolHeaderHttpsValue.equalsIgnoreCase(forwardedProtocols[i])) {
return false;
}
}
return true;
}
3. 解决方案
3.1. Spring过滤器来改写
启用:ForwardedHeaderFilter(注:spring默认不开启)
server: forward-headers-strategy: framework
或者
@Bean
FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter()
{
FilterRegistrationBean<ForwardedHeaderFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new ForwardedHeaderFilter());
return bean;
}
在最外层nginx配置信任边界代理
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Port $server_port; proxy_set_header X-Forwarded-Prefix /gateway/; # 如有url改写需配置
3.2. 通过增加独立Head变量传递
-
最外层Nginx
proxy_set_header Host $host:$server_port; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Url-Scheme $scheme; proxy_set_header X-Url-Port $server_port; proxy_set_header X-Forwarded-Host $http_x_forwarded_host; proxy_set_header X-Forwarded-Ssl on;
-
非最外层Nginx
proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header X-Url-Scheme $http_x_forwarded_proto; proxy_set_header X-Url-Port $http_x_forwarded_port;; proxy_set_header X-Forwarded-Host $http_x_forwarded_host; proxy_set_header X-Forwarded-Prefix $http_x_forwarded_prefix; # 如有url改写需配置 proxy_set_header X-Forwarded-Ssl on;
-
SpringBoot
server: tomcat: protocol-header: X-Url-Scheme port-header: X-Url-Port
3.3. ForwardedHeaderFilter无法解决nginx截断url的问题
- 改造ForwardedHeaderFilter
-
-
uri改写:原逻辑为:<prefix-path>/<PathWithinApplication>,新逻辑为:<prefix-path>/<servlet-context-path>/<PathWithinApplication>
-
contextPath改写:原逻辑为:<servlet-context-path>,新逻辑为:<prefix-path>/<servlet-context-path>
-
ForwardedHeaderFilter.ForwardedPrefixExtractor
@Nullable
private String initRequestUri() {
if (this.forwardedRequestUri != null) {
return this.forwardedRequestUri;
}
if (this.forwardedPrefix != null) {
return this.forwardedPrefix + this.delegate.get().getContextPath() + this.pathHelper.getPathWithinApplication(this.delegate.get());
}
return null;
}
public String getContextPath() {
return this.forwardedPrefix == null ? this.delegate.get().getContextPath() : this.forwardedPrefix + this.delegate.get().getContextPath();
}