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 }