对于安全检查,我需要访问资源服务中用户的远程IP地址。这个资源服务是一个简单的最近的Spring Boot应用程序,它使用我的Eureka服务器注册自己:
@SpringBootApplication
@EnableEurekaClient
public class ServletInitializer extends SpringBootServletInitializer {
public static void main(final String[] args) {
SpringApplication.run(ServletInitializer.class, args);
}
}
我的Eureka服务器注册的所有服务都是通过我的Zuul路由代理服务器根据Angel.SR3 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代理/ Apache服务器的地址,而不是远程客户端IP地址。
过去我曾使用org.springframework.security.authentication.AuthenticationDetailsSource
首选X-Forwarded-For
标头值而不是普通remoteAddress
来获取正确的IP地址,但我无法确定如何通过通过两个代理(Apache + Zuul)时,我的资源服务的正确远程IP地址。
任何人都可以帮助我访问这两个代理背后的正确远程IP地址,或者建议一种替代方法来实现这一点吗?
答案 0 :(得分:4)
原来X-Forwarded-For标头取自原始请求,进入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中的标头值,以防止仅接受用户提供的任何值:RequestHeader unset X-Forwarded-For
答案 1 :(得分:1)
通常,您可以使用servletRequest.getRemoteAddr()来获取访问Java Web应用程序的客户端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();
}