001 package org.springframework.richclient.security.remoting; 002 003 import org.apache.commons.httpclient.HttpClient; 004 import org.apache.commons.httpclient.UsernamePasswordCredentials; 005 import org.apache.commons.httpclient.auth.AuthScope; 006 import org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor; 007 import org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean; 008 import org.springframework.richclient.security.AuthenticationAware; 009 import org.springframework.security.Authentication; 010 011 /** 012 * Extension of <code>HttpInvokerProxyFactoryBean</code> that supports the use of BASIC 013 * authentication on each HTTP request while using commons-httpclient. 014 * Commons-httpclient can be easily configured to use SSL (so the BASIC authentication isn't sniffable): 015 * <code> 016 * ProtocolSocketFactory authSSLProtocolSocketFactory = new AuthSSLProtocolSocketFactory(null, null, 017 * truststoreUrl, TRUSTSTORE_PASSWORD); 018 * Protocol.registerProtocol("https", new Protocol("https", authSSLProtocolSocketFactory, 443)); 019 * </code> 020 * <p> 021 * This factory takes care of instantiating the proper invocation executor and keeping 022 * it up to date with the latest user credentials. Once a more complete AOP implementation 023 * is available, then this "token forwarding" can be removed as the default executor is 024 * already wired to receive notifications when it is constructed by the application 025 * context. 026 * <p> 027 * This configuration assumes that the user's credentials are "global" to the application 028 * and every invocation should use the same credentials. If you need per-thread 029 * authentication then you should look at using a combination of 030 * {@link org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean} and 031 * {@link org.springframework.security.context.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor}. 032 * <p> 033 * {@link org.springframework.richclient.security.AuthenticationAware} is implemented in order to get notifications of changes in 034 * the user's credentials. Please see the class documentation for 035 * <code>AuthenticationAware</code> to see how to configure the application context so 036 * that authentication changes are broadcast properly. 037 * <p> 038 * @author Geoffrey De Smet 039 * @author Larry Streepy 040 */ 041 public class BasicAuthCommonsHttpInvokerProxyFactoryBean extends HttpInvokerProxyFactoryBean implements AuthenticationAware { 042 043 /** 044 * Constructor. Install the default executor. 045 */ 046 public BasicAuthCommonsHttpInvokerProxyFactoryBean() { 047 setHttpInvokerRequestExecutor(new CommonsHttpInvokerRequestExecutor()); 048 } 049 050 051 /** 052 * Handle a change in the current authentication token. 053 * This method will fail fast if the executor isn't a CommonsHttpInvokerRequestExecutor. 054 * @see org.springframework.richclient.security.AuthenticationAware#setAuthenticationToken(org.springframework.security.Authentication) 055 */ 056 public void setAuthenticationToken(Authentication authentication) { 057 if( logger.isDebugEnabled() ) { 058 logger.debug("New authentication token: " + authentication); 059 } 060 061 CommonsHttpInvokerRequestExecutor executor 062 = (CommonsHttpInvokerRequestExecutor) getHttpInvokerRequestExecutor(); 063 HttpClient httpClient = executor.getHttpClient(); 064 httpClient.getParams().setAuthenticationPreemptive(authentication != null); 065 UsernamePasswordCredentials usernamePasswordCredentials; 066 if (authentication != null) { 067 usernamePasswordCredentials = new UsernamePasswordCredentials( 068 authentication.getName(), authentication.getCredentials().toString()); 069 } else { 070 usernamePasswordCredentials = null; 071 } 072 httpClient.getState().setCredentials(AuthScope.ANY, usernamePasswordCredentials); 073 } 074 }