【问题】
遇到内部管理非常严格的客户可能存在一套内部系统的部署规范,例如只支持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); } }