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