spring-remoting中的httpInvoker实现并安全验证


  1. 简单介绍httpInvoker的实现

           Spring中,HTTPInvoker(HTTP调用器)是通过基于HTTP协议的分布式远程调用解决方案.

           1.1客户端

                    a.向服务器发送远程调用请求:远程调用信息——>封装为远程调用对象——>序列化写入到远程调用HTTP请求中——>向服务器端发送。
                    b.接收服务器端返回的远程调用结果:服务器端返回的远程调用结果HTTP响应——>反序列化为远程调用结果对象。

          1.2服务端

                    a. 接收客户端发送的远程调用请求:客户端发送的远程调用HTTP请求——>反序列化为远程调用对象——>调用服务器端目标对象的目标方法处理。

                    b.向客户端返回远程调用结果: 服务器端目标对象方法的处理结果——>序列化写入远程调用结果HTTP响应中——>返回给客户端。
spring-remoting中的httpInvoker实现并安全验证_第1张图片

  2. 具体如何配置

    

      2.1 客户端配置





       ......

     
        
        
    



       2.2 服务端配置






.... 


 
     
     






     web.xml配置:

	
        remoting
        org.springframework.web.servlet.DispatcherServlet
        1
    

    
        remoting
        *.service
    



  2.3  调用测试

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方法,能输出对应的值。


  3. 添加验证

服务端部署一个安全的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实例的装配。

有了上面的分析,那么实现就变得简单了。

3.1 客户端配置





       ......

     
        
        
        
    


        
             
                
                 
             
        
    

 
 

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;
	}

}


3.2 服务端配置






.... 





 

 








3.3  调用测试

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() {
	}
	
	

}

上面如果验证不通过,直接写 return,这里可以按照自己的需要来处理。


配置web.xml

	
        remoting
        org.springframework.web.servlet.DispatcherServlet
        1
    

    
        remoting
        *.service
    


 
	remotingFilter
	 com.xxxx.common.AuthenticationFilter
	 
            userName
            abcdefgtest
        
        
            psw
            testpsw1234
        
	 
	
	 
	 	 remotingFilter
	  	*.service
	 
	 

网上有人说通过tomcat自带角色机制的验证也能实现服务端部分。。。


4. 关于spring-remoting调用的介绍 

详见:http://blog.csdn.net/lsblsb/article/details/40040385

为Hessian方式调用加密签名的安全机制  http://blog.csdn.net/luotangsha/article/details/6655555


你可能感兴趣的:(java基础和进阶,架构)