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    }