2016-07-31 229 views
8

我有一个连接到Web服务的CXF客户端。此客户端安装在同一网络中有两个IP地址的计算机上(例如172.16.1.101和172.16.1.102)。绑定CXF客户端源IP地址

如何配置CXF客户端使用特定的源IP地址,以便服务器看到来自该特定IP地址的请求而不是其他的IP地址?

如果我有进入插座,我会做这样的事情:

Socket s = new Socket(); 
s.bind(new InetSocketAddress("172.16.1.102", 0)); //this Ip address is the one I need to specify 
s.connect(new InetSocketAddress("google.com", 80)); 

是否有可能通过配置CXF创建这样我就可以指定源IP地址的套接字?

编辑:我需要指定源IP地址,因为在客户端和Web服务器之间有一个防火墙,其中一个IP地址的规则(来自其他IP地址的连接被阻止)。

回答

1

自定义URLStreamHandlerFactory的作品。

实施例:

 ... 
     URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory() { 
      @Override 
      public URLStreamHandler createURLStreamHandler(String protocol) { 
       if (protocol.equals("http")) { 
        return new HttpHandler(); 
       } else if (protocol.equals("https")) { 
        return new sun.net.www.protocol.https.Handler(); 
       } 
       return null; 
      } 
     }); 
     ... 

private class HttpHandler extends java.net.URLStreamHandler { 

    protected int getDefaultPort() { 
     return 80; 
    } 

    @Override 
    protected URLConnection openConnection(URL u) throws IOException { 
     return openConnection(u, (Proxy) null); 
    } 

    @Override 
    protected URLConnection openConnection(URL u, Proxy p) throws IOException { 
     return new HttpURLConnection(u, p) { 

      @Override 
      protected HttpClient getNewHttpClient(URL url, Proxy p, int connectTimeout) 
        throws IOException { 
       return new HttpClient(url, (String) null, -1, true, connectTimeout) { 

        @Override 
        protected Socket createSocket() throws IOException { 
         Socket s = new Socket(); 

         s.bind(new InetSocketAddress(InetAddress.getByName("1.2.3.4"), 
           0)); // yours IP here 

         return s; 
        } 

       }; 
      } 

      @Override 
      protected HttpClient getNewHttpClient(URL url, Proxy p, int connectTimeout, 
        boolean useCache) throws IOException { 
       return getNewHttpClient(url, p, connectTimeout); 
      } 

     }; 
    } 
} 

对于HTTPS也可用自定义的SSLSocketFactory

HttpsURLConnection.setDefaultSSLSocketFactory(...); 
+0

这是正确的答案,因为这是唯一可行的答案。抱歉,我已将bonty分配给另一个答案。一个重要的细节是,为了让https工作,您需要设置setUseHttpsURLConnectionDefaultSslSocketFactory(true)(在HTTPConduit TLSClientParameters中)。 – user2518618

0

不知道如果我理解正确的问题,但我不认为你需要在客户端上特别地设置的IP地址,服务器读取`

Message message = PhaseInterceptorChain.getCurrentMessage(); 
HttpServletRequest request = (HttpServletRequest)message.get(AbstractHTTPDestination.HTTP_REQUEST); 
request.getRemoteAddr() 

添加该代码在服务器上,它可以实际从请求中查找IP,您可以在IP上的服务器上进行比较。让我知道这是你在找什么。

编辑:-----

嗯,我的理解是防火墙没有看到进来头中的IP或有效载荷的消息。它会检查始发服务器的IP。

纠正我,如果我错了,但我觉得如果你部署客户端在该服务器上添加了防火墙规则,并打电话给服务器,没有理由它不应该工作。

让我知道你正在得到什么样的错误,说一个堆栈跟踪什么的,我们可以看到发生了什么。

+0

我编辑的问题。我需要指定源IP地址,因为在客户端和Web服务器之间有一个防火墙,其中包含一个IP地址的规则,而不是另一个。如果连接不是来自正确的IP地址,它不会通过防火墙 – user2518618

+0

检查我编辑的答案 –

+0

您是对的,防火墙查看IP报头,而不是消息的有效负载。这就是为什么我必须在Socket中设置IP。我从客户端收到的错误是超时错误,因为防火墙不转发数据包。如果一台机器有两个同一网络的IP地址,则可以配置打开每个套接字时使用哪一个IP地址。 CXF框架负责处理包括Socket(和源IP地址)在内的所有内容,但我需要一种方法来配置此套接字(如果可能的话)。 – user2518618

5

CXF客户端使用java.net.URLConnection连接到服务。该URLConnection的可配置选择这样一个本地IP地址(请参阅How can I specify the local address on a java.net.URLConnection?

URL url = new URL(yourUrlHere); 
Proxy proxy = new Proxy(Proxy.Type.DIRECT, 
    new InetSocketAddress( 
     InetAddress.getByAddress(
      new byte[]{your, ip, interface, here}), yourTcpPortHere)); 
URLConnection conn = url.openConnection(proxy); 

我曾考察文物cxf-rt-rs-clientcxf-rt-transports-http的代码,看看CXF是如何创建的连接。在ProxyFactory是创建所需UrlConnection

private Proxy createProxy(final HTTPClientPolicy policy) { 
    return new Proxy(Proxy.Type.valueOf(policy.getProxyServerType().toString()), 
        new InetSocketAddress(policy.getProxyServer(), 
              policy.getProxyServerPort())); 
} 

正如你可以看到Proxy对象的代码,有没有办法来配置IP地址,所以我恐怕这个问题的答案是可以不CXF

配置源IP地址,但是,我认为它不会是很难修改的源代码,允许设置的源IP地址

HTTPClientPolicy

添加以下代码以org.apache.cxf.transports.http.configuration.HTTPClientPolicycxf-rt-transports-http

public class HTTPClientPolicy { 
    protected byte[] sourceIPAddress; 
    protected int port; 

    public boolean isSetSourceIPAddress(){ 
     return (this.sourceIPAddress != null); 
    } 

ProxyFactory里

修改下面的代码来org.apache.cxf.transport.http.ProxyFactorycxf-rt-transports-http

//added || policy.isSetSourceIPAddress() 
//getProxy() calls finally to createProxy 
public Proxy createProxy(HTTPClientPolicy policy, URI currentUrl) { 
    if (policy != null) { 
     // Maybe the user has provided some proxy information 
     if (policy.isSetProxyServer() || policy.isSetSourceIPAddress()) 
      && !StringUtils.isEmpty(policy.getProxyServer())) { 
      return getProxy(policy, currentUrl.getHost()); 
     } else { 
      // There is a policy but no Proxy configuration, 
      // fallback on the system proxy configuration 
      return getSystemProxy(currentUrl.getHost()); 
     } 
    } else { 
     // Use system proxy configuration 
     return getSystemProxy(currentUrl.getHost()); 
    } 
} 

//Added condition to set the source IP address (is set) 
//Will work also with a proxy 
private Proxy createProxy(final HTTPClientPolicy policy) { 
    if (policy.isSetSourceIPAddress()){ 
     Proxy proxy = new Proxy(Proxy.Type.DIRECT, 
      new InetSocketAddress( 
       InetAddress.getByAddress(
        policy.getSourceIPAddress(), policy.getPort())); 
    } else { 
     return new Proxy(Proxy.Type.valueOf(policy.getProxyServerType().toString()), 
        new InetSocketAddress(policy.getProxyServer(), 
              policy.getProxyServerPort())); 
    }     
} 

用法

Client client = ClientProxy.getClient(service); 
HTTPConduit http = (HTTPConduit) client.getConduit(); 
HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy(); 
httpClientPolicy.setSourceIPAddress(new byte[]{your, ip, interface, here})); 
httpClientPolicy.setPort(yourTcpPortHere); 
http.setClient(httpClientPolicy); 
+0

看着你的答案我想也许httpClientPolicy。 setProxyServer(...)可以工作(不需要修改CXF),因为原始的createProxy()使用它。但它不起作用,因为没有办法设置Proxy.Type.DIRECT,并且ProxyServer也在ProxyFactory的其他部分中使用(以设置实际代理)。因此,您的答案完全正确,设置源IP地址的唯一方法是修改CXF源代码。这是一个非常完整的答案。谢谢! – user2518618

+0

我试图修改您建议的CXF代码,但它不起作用。无法使用Proxy.Type.DIRECT创建新的代理服务器。如果你看[代理代码](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/net/Proxy.java#Proxy.% 3Cinit%3E%28java.net.Proxy.Type%2Cjava.net.SocketAddress%29)如果参数是DIRECT,则会引发异常。另请参阅[如何在java.net.URLConnection上指定本地地址?]的答案中的注释(http://stackoverflow.com/questions/91678/how-can-i-specify-the-local- address-on-a-java-net-urlconnection) – user2518618

+0

你是绝对正确的。自从JDK1以来,'Proxy'类通常会引发类型为DIRECT的异常。5(我还没有查看更多版本)。在http://stackoverflow.com/questions/91678/how-can-i-specify-the-local-address-on-a-java-net-urlconnection中链接的代码无法工作。我尝试子类'Proxy',但是'URLConnection'克隆代理并失败。 – pedrofb