通过 Zuul 和 Apache 代理访问资源服务器中的远程 IP 地址
Access remote IP address in resource server proxied through Zuul and Apache
为了进行安全检查,我需要访问我的资源服务中用户的远程 IP 地址。这个资源服务是一个简单的最近 Spring 启动应用程序,它在我的 Eureka 服务器上注册了自己:
@SpringBootApplication
@EnableEurekaClient
public class ServletInitializer extends SpringBootServletInitializer {
public static void main(final String[] args) {
SpringApplication.run(ServletInitializer.class, args);
}
}
注册到我的Eureka服务器的所有服务都通过我基于Angel.SR3的Zuul路由代理服务器动态路由starter-zuul
和starter-eureka
:
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
public class RoutingProxyServer {
public static void main(final String[] args) {
SpringApplication.run(RoutingProxyServer.class, args);
}
}
Zuul路由代理服务器也为下一步配置AJP连接器:
@Configuration
@ConditionalOnProperty("ajp.port")
public class TomcatAjpConfig extends TomcatWebSocketContainerCustomizer {
@Value("${ajp.port}")
private int port;
@Override
public void doCustomize(final TomcatEmbeddedServletContainerFactory tomcat) {
super.doCustomize(tomcat);
// Listen for AJP requests
Connector ajp = new Connector("AJP/1.3");
ajp.setPort(port);
tomcat.addAdditionalTomcatConnectors(ajp);
}
}
对动态路由 zuul 代理的所有请求都通过 Apache 进行代理,以在标准 443 端口上提供 HTTPS:
# Preserve Host when proxying so jar apps return working URLs in JSON responses
RequestHeader set X-Forwarded-Proto "https"
ProxyPreserveHost On
# Redirect remaining traffic to routing proxy server
ProxyPass / ajp://192.168.x.x:8009/
# Also update Location, Content-Location and URI headers on HTTP redirect responses
ProxyPassReverse / ajp://192.168.x.x:8009/
所有这些就绪后,资源服务就可用了,但不幸的是,我从 Spring Security 获得的 remoteAddress 是 Zuul proxy/Apache 服务器的地址,而不是远程客户端 IP 地址.
过去我使用 org.springframework.security.authentication.AuthenticationDetailsSource
比正常 remoteAddress
更喜欢 X-Forwarded-For
header 值来获得正确的 IP 地址,但我不能找出在通过两个代理(Apache + Zuul)时如何将正确的远程 IP 地址传递给我的资源服务。
任何人都可以帮助我访问这两个代理背后的正确远程 IP 地址,或者建议一种替代方法来实现它吗?
通常,您可以使用 servletRequest.getRemoteAddr() 获取访问您的 Java 网络应用程序的客户端 IP 地址。
String ipAddress = request.getRemoteAddr();
但是,如果用户在代理服务器后面或通过负载平衡器访问您的 Web 服务器(例如,在云托管中),上面的代码将获取代理服务器或负载平衡器服务器的 IP 地址,而不是客户端的原始IP地址。
要解决它,你应该得到请求的HTTP头“X-Forwarded-For (XFF)”的IP地址。
String ipAddress = request.getHeader("X-FORWARDED-FOR");
if (ipAddress == null) {
ipAddress = request.getRemoteAddr();
}
原来 X-Forwarded-For header 是从原始请求中提取的,该请求馈送到 Zuul 以填充 HttpServletRequest#getRemoteAddr()
。然后必须通过 RequestContext#getZuulRequestHeaders().put("X-Forwarded-For", remoteAddr)
将其传递给代理后端服务。下面的 ZuulFilter
实现了这一点,即使它还没有将自己的值附加到 X-Forwarded-For
过滤器。
@Component
@Slf4j
public class XForwardedForFilter extends ZuulFilter {
private static final String X_FORWARDED_FOR = "X-Forwarded-For";
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
// Rely on HttpServletRequest to retrieve the correct remote address from upstream X-Forwarded-For header
HttpServletRequest request = ctx.getRequest();
String remoteAddr = request.getRemoteAddr();
// Pass remote address downstream by setting X-Forwarded for header again on Zuul request
log.debug("Settings X-Forwarded-For to: {}", remoteAddr);
ctx.getZuulRequestHeaders().put(X_FORWARDED_FOR, remoteAddr);
return null;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 10000;
}
}
有人可能想在代理到 Zuul 之前清除 Apache 中的 header 值,以防止只接受用户提供的任何值:RequestHeader unset X-Forwarded-For
为了进行安全检查,我需要访问我的资源服务中用户的远程 IP 地址。这个资源服务是一个简单的最近 Spring 启动应用程序,它在我的 Eureka 服务器上注册了自己:
@SpringBootApplication
@EnableEurekaClient
public class ServletInitializer extends SpringBootServletInitializer {
public static void main(final String[] args) {
SpringApplication.run(ServletInitializer.class, args);
}
}
注册到我的Eureka服务器的所有服务都通过我基于Angel.SR3的Zuul路由代理服务器动态路由starter-zuul
和starter-eureka
:
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
public class RoutingProxyServer {
public static void main(final String[] args) {
SpringApplication.run(RoutingProxyServer.class, args);
}
}
Zuul路由代理服务器也为下一步配置AJP连接器:
@Configuration
@ConditionalOnProperty("ajp.port")
public class TomcatAjpConfig extends TomcatWebSocketContainerCustomizer {
@Value("${ajp.port}")
private int port;
@Override
public void doCustomize(final TomcatEmbeddedServletContainerFactory tomcat) {
super.doCustomize(tomcat);
// Listen for AJP requests
Connector ajp = new Connector("AJP/1.3");
ajp.setPort(port);
tomcat.addAdditionalTomcatConnectors(ajp);
}
}
对动态路由 zuul 代理的所有请求都通过 Apache 进行代理,以在标准 443 端口上提供 HTTPS:
# Preserve Host when proxying so jar apps return working URLs in JSON responses
RequestHeader set X-Forwarded-Proto "https"
ProxyPreserveHost On
# Redirect remaining traffic to routing proxy server
ProxyPass / ajp://192.168.x.x:8009/
# Also update Location, Content-Location and URI headers on HTTP redirect responses
ProxyPassReverse / ajp://192.168.x.x:8009/
所有这些就绪后,资源服务就可用了,但不幸的是,我从 Spring Security 获得的 remoteAddress 是 Zuul proxy/Apache 服务器的地址,而不是远程客户端 IP 地址.
过去我使用 org.springframework.security.authentication.AuthenticationDetailsSource
比正常 remoteAddress
更喜欢 X-Forwarded-For
header 值来获得正确的 IP 地址,但我不能找出在通过两个代理(Apache + Zuul)时如何将正确的远程 IP 地址传递给我的资源服务。
任何人都可以帮助我访问这两个代理背后的正确远程 IP 地址,或者建议一种替代方法来实现它吗?
通常,您可以使用 servletRequest.getRemoteAddr() 获取访问您的 Java 网络应用程序的客户端 IP 地址。
String ipAddress = request.getRemoteAddr();
但是,如果用户在代理服务器后面或通过负载平衡器访问您的 Web 服务器(例如,在云托管中),上面的代码将获取代理服务器或负载平衡器服务器的 IP 地址,而不是客户端的原始IP地址。
要解决它,你应该得到请求的HTTP头“X-Forwarded-For (XFF)”的IP地址。
String ipAddress = request.getHeader("X-FORWARDED-FOR");
if (ipAddress == null) {
ipAddress = request.getRemoteAddr();
}
原来 X-Forwarded-For header 是从原始请求中提取的,该请求馈送到 Zuul 以填充 HttpServletRequest#getRemoteAddr()
。然后必须通过 RequestContext#getZuulRequestHeaders().put("X-Forwarded-For", remoteAddr)
将其传递给代理后端服务。下面的 ZuulFilter
实现了这一点,即使它还没有将自己的值附加到 X-Forwarded-For
过滤器。
@Component
@Slf4j
public class XForwardedForFilter extends ZuulFilter {
private static final String X_FORWARDED_FOR = "X-Forwarded-For";
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
// Rely on HttpServletRequest to retrieve the correct remote address from upstream X-Forwarded-For header
HttpServletRequest request = ctx.getRequest();
String remoteAddr = request.getRemoteAddr();
// Pass remote address downstream by setting X-Forwarded for header again on Zuul request
log.debug("Settings X-Forwarded-For to: {}", remoteAddr);
ctx.getZuulRequestHeaders().put(X_FORWARDED_FOR, remoteAddr);
return null;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 10000;
}
}
有人可能想在代理到 Zuul 之前清除 Apache 中的 header 值,以防止只接受用户提供的任何值:RequestHeader unset X-Forwarded-For