【问题】
遇到内部管理非常严格的客户可能存在一套内部系统的部署规范,例如只支持HTTPS协议不支持HTTP,如果系统多出采用HttpInvoker,而此处的配置大多不能直接支持HTTPS。我们可以建议客户同时开放HTTP和HTTPS,对外只开放HTTPS端口,此时物理服务器内部的应用可以使用HTTP协议进行HttpInvoker交互,但如果是分布式部署呢?
【解决办法】
1. 大多的配置如下(很多程序都采用如下配置方式),此时只能支持HTTP协议,不支持HTTPS:
<bean id="remoteService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
<!-- 远程服务的url-->
<property name="serviceUrl" value="${tt.server}/remoteService.remoting" />
<!-- 远程服务所实现的接口-->
<property name="serviceInterface" value="org.kevin.SimpleService" />
</bean>
2. 可以调整为如下,使其同时支持HTTP和HTTPS:
<bean id="remoteService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
<!-- 远程服务的url-->
<property name="serviceUrl" value="${tt.server}/remoteService.remoting" />
<!-- 远程服务所实现的接口-->
<property name="serviceInterface" value="org.kevin.SimpleService" />
<property name="httpInvokerRequestExecutor">
<bean class="org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor"></bean>
</property>
</bean>
但问题是又来了,这个httpInvokerRequestExecutor使用的是HttpClient,而这个家伙要求你必须配置证书文件(配置方法很复杂,还要考虑证书过期更新问题)。
3. 怎么办!?改写一下CommonsHttpInvokerRequestExecutor ,让他即支持HTTPS同时还不校验证书(有点安全隐患,不过用该可以接收),于是采用如下配置:
<bean id="remoteService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
<!-- 远程服务的url-->
<property name="serviceUrl" value="${tt.server}/remoteService.remoting" />
<!-- 远程服务所实现的接口-->
<property name="serviceInterface" value="org.kevin.SimpleService" />
<property name="httpInvokerRequestExecutor">
<bean class="org.kevin.KevinCommonsHttpInvokerRequestExecutor"></bean>
</property>
</bean>
【示例代码】
package org.kevin;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.commons.httpclient.ConnectTimeoutException;
import org.apache.commons.httpclient.HttpClientError;
import org.apache.commons.httpclient.params.HttpConnectionParams;
import org.apache.commons.httpclient.protocol.ControllerThreadSocketFactory;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
import org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor;
/**
* <p>
* Title: HttpInvoker的自定义httpInvokerRequestExecutor实现
* </p>
*
* <p>
* Description: 支持HTTP和HTTPS,同时HTTPS不进行证书的校验
* </p>
*
* <p>
* Company: 北京九恒星科技股份有限公司
* </p>
*
* @author li.wenkai
*
* @since:2011-10-18 下午03:37:03
*
*/
public class KevinCommonsHttpInvokerRequestExecutor extends CommonsHttpInvokerRequestExecutor {
static {
ProtocolSocketFactory fcty = new MySecureProtocolSocketFactory();
Protocol.registerProtocol("https", new Protocol("https", fcty, 443));
}
}
class MyX509TrustManager implements X509TrustManager {
/*
* (non-Javadoc)
*
* @see
* javax.net.ssl.X509TrustManager#checkClientTrusted(java.security.cert.
* X509Certificate[], java.lang.String)
*/
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}
/*
* (non-Javadoc)
*
* @see
* javax.net.ssl.X509TrustManager#checkServerTrusted(java.security.cert.
* X509Certificate[], java.lang.String)
*/
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}
/*
* (non-Javadoc)
*
* @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()
*/
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public boolean isClientTrusted(X509Certificate[] arg0) {
return false;
}
public boolean isServerTrusted(X509Certificate[] arg0) {
return false;
}
}
class MySecureProtocolSocketFactory implements SecureProtocolSocketFactory {
private SSLContext sslContext = null;
/**
* Constructor for MySecureProtocolSocketFactory.
*/
public MySecureProtocolSocketFactory() {
}
/**
*
* @return
*/
private static SSLContext createEasySSLContext() {
try {
SSLContext context = SSLContext.getInstance("SSL");
context.init(null, new TrustManager[] { new MyX509TrustManager() }, null);
return context;
} catch (Exception e) {
throw new HttpClientError(e.toString());
}
}
/**
*
* @return
*/
private SSLContext getSSLContext() {
if (this.sslContext == null) {
this.sslContext = createEasySSLContext();
}
return this.sslContext;
}
/*
* (non-Javadoc)
*
* @see
* org.apache.commons.httpclient.protocol.ProtocolSocketFactory#createSocket
* (java.lang.String, int, java.net.InetAddress, int)
*/
public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException,
UnknownHostException {
return getSSLContext().getSocketFactory().createSocket(host, port, clientHost, clientPort);
}
/*
* (non-Javadoc)
*
* @see
* org.apache.commons.httpclient.protocol.ProtocolSocketFactory#createSocket
* (java.lang.String, int, java.net.InetAddress, int,
* org.apache.commons.httpclient.params.HttpConnectionParams)
*/
public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort,
final HttpConnectionParams params) throws IOException, UnknownHostException, ConnectTimeoutException {
if (params == null) {
throw new IllegalArgumentException("Parameters may not be null");
}
int timeout = params.getConnectionTimeout();
if (timeout == 0) {
return createSocket(host, port, localAddress, localPort);
} else {
return ControllerThreadSocketFactory.createSocket(this, host, port, localAddress, localPort, timeout);
}
}
/*
* (non-Javadoc)
*
* @see SecureProtocolSocketFactory#createSocket(java.lang.String,int)
*/
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return getSSLContext().getSocketFactory().createSocket(host, port);
}
/*
* (non-Javadoc)
*
* @see
* SecureProtocolSocketFactory#createSocket(java.net.Socket,java.lang.String
* ,int,boolean)
*/
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException,
UnknownHostException {
return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);
}
}