org.valkyriercp.security.remoting.BasicAuthCommonsHttpInvokerProxyFactoryBean.java Source code

Java tutorial

Introduction

Here is the source code for org.valkyriercp.security.remoting.BasicAuthCommonsHttpInvokerProxyFactoryBean.java

Source

/**
 * Copyright (C) 2015 Valkyrie RCP
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.valkyriercp.security.remoting;

import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.auth.*;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
import org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor;
import org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean;
import org.springframework.security.core.Authentication;
import org.valkyriercp.security.AuthenticationAware;

import java.io.IOException;

/**
 * Extension of <code>HttpInvokerProxyFactoryBean</code> that supports the use of BASIC
 * authentication on each HTTP request while using commons-httpclient.
 * Commons-httpclient can be easily configured to use SSL (so the BASIC authentication isn't sniffable):
 * <code>
 *       ProtocolSocketFactory authSSLProtocolSocketFactory = new AuthSSLProtocolSocketFactory(null, null,
 *               truststoreUrl, TRUSTSTORE_PASSWORD);
 *       Protocol.registerProtocol("https", new Protocol("https", authSSLProtocolSocketFactory, 443));
 * </code>
 * <p>
 * This factory takes care of instantiating the proper invocation executor and keeping
 * it up to date with the latest user credentials. Once a more complete AOP implementation
 * is available, then this "token forwarding" can be removed as the default executor is
 * already wired to receive notifications when it is constructed by the application
 * context.
 * <p>
 * This configuration assumes that the user's credentials are "global" to the application
 * and every invocation should use the same credentials. If you need per-thread
 * authentication then you should look at using a combination of
 * {@link org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean} and
 * AuthenticationSimpleHttpInvokerRequestExecutor.
 * <p>
 * {@link AuthenticationAware} is implemented in order to get notifications of changes in
 * the user's credentials. Please see the class documentation for
 * <code>AuthenticationAware</code> to see how to configure the application context so
 * that authentication changes are broadcast properly.
 * <p>
 * @author Geoffrey De Smet
 * @author Larry Streepy
 */
public class BasicAuthCommonsHttpInvokerProxyFactoryBean extends HttpInvokerProxyFactoryBean
        implements AuthenticationAware {

    /**
     * Constructor. Install the default executor.
     */
    public BasicAuthCommonsHttpInvokerProxyFactoryBean() {
        setHttpInvokerRequestExecutor(new HttpComponentsHttpInvokerRequestExecutor());
    }

    /**
     * Handle a change in the current authentication token.
     * This method will fail fast if the executor isn't a CommonsHttpInvokerRequestExecutor.
     * @see org.valkyriercp.security.AuthenticationAware#setAuthenticationToken(org.springframework.security.core.Authentication)
     */
    public void setAuthenticationToken(Authentication authentication) {
        if (logger.isDebugEnabled()) {
            logger.debug("New authentication token: " + authentication);
        }

        HttpComponentsHttpInvokerRequestExecutor executor = (HttpComponentsHttpInvokerRequestExecutor) getHttpInvokerRequestExecutor();
        DefaultHttpClient httpClient = (DefaultHttpClient) executor.getHttpClient();
        BasicCredentialsProvider provider = new BasicCredentialsProvider();
        httpClient.setCredentialsProvider(provider);
        httpClient.addRequestInterceptor(new PreemptiveAuthInterceptor());
        UsernamePasswordCredentials usernamePasswordCredentials;
        if (authentication != null) {
            usernamePasswordCredentials = new UsernamePasswordCredentials(authentication.getName(),
                    authentication.getCredentials().toString());
        } else {
            usernamePasswordCredentials = null;
        }
        provider.setCredentials(AuthScope.ANY, usernamePasswordCredentials);
    }

    static class PreemptiveAuthInterceptor implements HttpRequestInterceptor {

        public void process(final HttpRequest request, final HttpContext context)
                throws HttpException, IOException {
            AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);

            if (authState.getAuthScheme() == null) {
                AuthScheme authScheme = (AuthScheme) context.getAttribute("preemptive-auth");
                CredentialsProvider credsProvider = (CredentialsProvider) context
                        .getAttribute(ClientContext.CREDS_PROVIDER);
                HttpHost targetHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
                if (authScheme != null) {
                    Credentials creds = credsProvider
                            .getCredentials(new AuthScope(targetHost.getHostName(), targetHost.getPort()));
                    if (creds == null) {
                        throw new HttpException("No credentials for preemptive authentication");
                    }
                    authState.setAuthScheme(authScheme);
                    authState.setCredentials(creds);
                }
            }

        }

    }
}