Spring中,HTTPInvoker(HTTP调用器)是通过基于HTTP协议的分布式远程调用解决方案.
a.向服务器发送远程调用请求:远程调用信息——>封装为远程调用对象——>序列化写入到远程调用HTTP请求中——>向服务器端发送。
b.接收服务器端返回的远程调用结果:服务器端返回的远程调用结果HTTP响应——>反序列化为远程调用结果对象。
a. 接收客户端发送的远程调用请求:客户端发送的远程调用HTTP请求——>反序列化为远程调用对象——>调用服务器端目标对象的目标方法处理。
b.向客户端返回远程调用结果: 服务器端目标对象方法的处理结果——>序列化写入远程调用结果HTTP响应中——>返回给客户端。
......
....
web.xml配置:
remoting
org.springframework.web.servlet.DispatcherServlet
1
remoting
*.service
public class TestRemoting extends BaseUxiang {
private TestService testService;
public void testRemoting() {
long productId = 1224L;
ProductDO productDO = testService.queryById(productId);
System.out.println("productDO -> "+productDO);
if(productDO != null) {
System.out.println("info-->" + productDO.getId() + "," + productDO.getName());
}
}
public void setTestService(TestService testService) {
this.testService = testService;
}
import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
/**
* @author niepeng
*
* @date 2012-9-4 上午11:08:48
*/
public class BaseUxiang extends AbstractTransactionalDataSourceSpringContextTests {
@Override
protected String[] getConfigLocations() {
// 当配置多个dataSource的时候,该行解决:No unique bean of type [javax.sql.DataSource]
super.setAutowireMode(AUTOWIRE_BY_NAME);
return new String[] {"applicationContext.xml"};
}
}
启动服务端,然后客户端执行testRemoting方法,能输出对应的值。
服务端部署一个安全的HTTP服务是件很容易的事情,添加一些限制条件就可以了。
客户端实现上,正常来说也是比较简单的,但是现在调用封装是spring内部实现的,就拿HttpInvokerProxyFactoryBean来说,这个Bean可以由内置的JDK HTTP支持,也可由commons httpClient project来支持,内置的JDK HTTP不支持HTTP基本验证,这意味着你需要使用HttpClient去访问一个安全服务,配置HttpInvokerProxyFactoryBean,我们需要使用HttpClient的CommonsHttpInvokerRequestExecutor的实例中对httpInvokerRequestExecutor的特征进行下一步设置这将事情变得复杂了。
CommonsHttpInvokerRequestExecutor不允许你将用户名和密码作为属性设置,但是他确实允许你访问HttpClient这个类所产生的实例,这是HttpClient project的核心。但是,你不可能使用Spring的依赖注入配置HttpClient的基本认证功能的凭证(因为不是setter/getter属性),所以,我们使用了Spring的FactoryBean返回一个HttpClient的恰当配置的实例,我们用一个HttpClientFactoryBean类来完成HttpClient实例的装配。
有了上面的分析,那么实现就变得简单了。
......
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
public class HttpClientFactoryBean implements InitializingBean, FactoryBean {
private HttpClient httpClient;
private String username;
private String password;
private String authenticationHost;
public HttpClient getHttpClient() {
return httpClient;
}
public void setHttpClient(HttpClient httpClient) {
this.httpClient = httpClient;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getAuthenticationHost() {
return authenticationHost;
}
public void setAuthenticationHost(String authenticationHost) {
this.authenticationHost = authenticationHost;
}
public void afterPropertiesSet() throws Exception {
// 构造HttpClient对象
// httpClient = new HttpClient();
// 特别注意:这里需要使用多线程,因为容器初始化后,每次请求都是通过这个httpclient请求去实现的,所以需要多线程
httpClient = new HttpClient(new MultiThreadedHttpConnectionManager());
// httpClient.getParams().setParameter("username", username);
// httpClient.getParams().setParameter("password", password);
// System.out.println("afterPropertiesSet->username=" + username + ", password=" + password);
httpClient.getState().setAuthenticationPreemptive(true);
Credentials credentials = new UsernamePasswordCredentials(username, password);
httpClient.getState().setCredentials(null, authenticationHost, credentials);
}
public Object getObject() throws Exception {
return httpClient;
}
public Class getObjectType() {
return HttpClient.class;
}
public boolean isSingleton() {
return true;
}
}
....
public class TestRemoting extends BaseUxiang {
private TestService testService;
public void testRemoting() {
long productId = 1224L;
ProductDO productDO = testService.queryById(productId);
System.out.println("productDO -> "+productDO);
if(productDO != null) {
System.out.println("info-->" + productDO.getId() + "," + productDO.getName());
}
}
public void setTestService(TestService testService) {
this.testService = testService;
}
import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
/**
* @author niepeng
*
* @date 2012-9-4 上午11:08:48
*/
public class BaseUxiang extends AbstractTransactionalDataSourceSpringContextTests {
@Override
protected String[] getConfigLocations() {
// 当配置多个dataSource的时候,该行解决:No unique bean of type [javax.sql.DataSource]
super.setAutowireMode(AUTOWIRE_BY_NAME);
return new String[] {"applicationContext.xml"};
}
}
到当前为止,客户端调用的时候,已经添加验证信息了,接下来服务端处理。
这里是通过filter来验证。
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Decoder;
public class AuthenticationFilter implements Filter {
private String userName;
private String psw;
private static final Logger log = LoggerFactory.getLogger(AuthenticationFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
userName = filterConfig.getInitParameter("userName");
psw = filterConfig.getInitParameter("psw");
log.error("init username and psw=" + userName + "," + psw);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String authorization = req.getHeader("authorization");
if (authorization == null) {
log.error("remoting method request authorization is null");
return;
}
if (!authorization.startsWith("Basic")) {
log.error("remoting method request authorization is not start with basic");
return;
}
authorization = authorization.substring(authorization.indexOf(' ') + 1);
BASE64Decoder decoder = new BASE64Decoder();
byte[] bytes = decoder.decodeBuffer(authorization);
authorization = new String(bytes);
String parseUserName = authorization.substring(0, authorization.indexOf(':'));
String parsePassword = authorization.substring(authorization.indexOf(':') + 1);
if (!userName.equals(parseUserName) || !psw.equals(parsePassword)) {
log.error("remoting method request authorization username or psw is not match");
return;
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
配置web.xml
remoting
org.springframework.web.servlet.DispatcherServlet
1
remoting
*.service
remotingFilter
com.xxxx.common.AuthenticationFilter
userName
abcdefgtest
psw
testpsw1234
remotingFilter
*.service
网上有人说通过tomcat自带角色机制的验证也能实现服务端部分。。。
详见:http://blog.csdn.net/lsblsb/article/details/40040385
为Hessian方式调用加密签名的安全机制 http://blog.csdn.net/luotangsha/article/details/6655555