There is a request from client to restrict a limit of machines' IPs to access the web service developed by spring and hessian.
Definitely, There is no out-of-the-box function for Spring or Hessian to open HttpServletRequest to use.
Some smart guys have found some ways to resolve this problem.
Solution 1) modify the Hessian source code
refer to http://www.blogjava.net/Caixiaopig/archive/2007/08/03/134287.aspx
Solution 2) develop a customized HessianExporter
refer to http://wesee.javaeye.com/blog/663876
I prefer the second solution.
// HessianContext.java
package com.spring.hessian.test.exporter;
import javax.servlet.ServletRequest;
public class HessianContext {
private ServletRequest _request;
private static final ThreadLocal<HessianContext> _localContext = new ThreadLocal<HessianContext>() {
public HessianContext initialValue() {
return new HessianContext();
}
};
private HessianContext() {
}
public static void setRequest(ServletRequest request) {
_localContext.get()._request = request;
}
public static ServletRequest getRequest() {
return _localContext.get()._request;
}
public static void clear() {
_localContext.get()._request = null;
}
}
// customized Hessian Exporter
package com.spring.hessian.test.exporter;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.remoting.caucho.HessianExporter;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.util.NestedServletException;
public class MyExporter extends HessianExporter implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
if (!"POST".equals(request.getMethod())) {
throw new HttpRequestMethodNotSupportedException(request
.getMethod(), new String[] { "POST" },
"HessianServiceExporter only supports POST requests");
}
response.setContentType(CONTENT_TYPE_HESSIAN);
try {
String clientIp = request.getRemoteAddr();
System.out.println("----------->>> "+clientIp);
if (!clientIp.equalsIgnoreCase("10.12.103.118")){
throw new Exception("Invalid IP-----------------------");
}
HessianContext.setRequest(request);
invoke(request.getInputStream(), response.getOutputStream());
} catch (Throwable ex) {
throw e;
} finally {
HessianContext.clear();
}
}
}
// your <servlet-name>-servlet.xml
take note the class name in red, should put your customized hessian exporter class,not standard one
<bean name="/onepassService"
class="com.spring.hessian.test.exporter.MyExporter">
<property name="service" ref="accountInfoImpl" />
<property name="serviceInterface" value="com.spring.hessian.test.IAccountInfo" />
</bean>
I wonder how this guy worked it out ! :-(
2) can not get Spring Bean if using auto classpath scan
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean
class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="order" value="1" />
</bean>
<bean
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="defaultHandler" ref="httpRequestHandlerAdapter" />
<property name="order" value="2" />
</bean>
<bean id="httpRequestHandlerAdapter"
class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter" />
<context:component-scan base-package="com.spring.hessian.test" />
</beans>
The three beans definition are required, otherwise, NullPointerException will occur due to system can not find the spring beans if adopting annotation to define spring bean.
3) setup SSL connection
3.1 generate keystore
keytool -genkeypair -keyalg RSA -keysize 2048 -sigalg SHA1withRSA -validity 36000 -storepass changeit -alias myalias -keystore my.keystore -dname "CN=localhost,OU=mycomp,O=mycomp,L=SG,ST=SG,C=SG"
TAKE NOTE: set the value of CN carefully. if set CN=myhost, the URL of hessian servlet would be
https://myhost:port/xxxxxxxx3.2 configure SSL connection in tomcat
edit %tomcat%/conf/server.xml and add following configuration
<Connector port="8843" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS"
keystoreFile="C:/tmp/my.keystore" keystorePass="changeit" />
3.3 export cert for client side
keytool -exportcert -alias myalias -keystore my.keystore -file my.pem -rfc -storepass changeit
3.4 import cert to client's trust store
keytool -import -trustcacerts -alias mycert_tomcat -file c:\tmp\my.pem -keystore cacerts
3.5 client caller source code
package com.spring.hessian.test;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import com.caucho.hessian.client.HessianProxyFactory;
public class HessianTester {
@BeforeClass
public static void setUpBeforeClass() throws Exception {
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
}
@Test
public void test() throws Exception {
System.setProperty("java.protocol.handler.pkgs", "javax.net.ssl");
System.setProperty("javax.net.ssl.trustStore", "C:\\tmp\\cacerts");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
String url = "https://localhost:8843/springhessian/remote/myService";
HessianProxyFactory factory = new HessianProxyFactory();
IAccountInfo accountInfo = (IAccountInfo) factory.create(
IAccountInfo.class, url);
accountInfo.changeEmail(126, "def@email.com");
accountInfo.deleteAccount(129);
}
}
take note the property in red, previously, I set it as keyStore and always got the exception as follows.
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(Unknown Source)
at sun.security.validator.PKIXValidator.engineValidate(Unknown Source)
at sun.security.validator.Validator.validate(Unknown Source)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(Unknown Source)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
... 38 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source)
at java.security.cert.CertPathBuilder.build(Unknown Source)
... 44 more