通过Zuul和Apache代理访问资源服务器中的远程IP地址

时间:2015-08-27 19:48:14

标签: apache spring-security spring-cloud netflix-zuul

对于安全检查,我需要访问资源服务中用户的远程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-zuulstarter-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地址,或者建议一种替代方法来实现这一点吗?

2 个答案:

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