/**
* 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.commons.codec.binary.Base64;
import org.springframework.remoting.httpinvoker.SimpleHttpInvokerRequestExecutor;
import org.springframework.security.core.Authentication;
import org.valkyriercp.security.AuthenticationAware;
import java.io.IOException;
import java.net.HttpURLConnection;
/**
* Adds BASIC authentication support to
* <code>SimpleHttpInvokerRequestExecutor</code>. This class assumes that a
* single authentication credential will be used for the whole application (as
* is typical with a rich client). So, regardless of the thread involved, it
* will use the Authentication object obtained from the last authentication
* token received.
* <p>
* In comparison, see
* {@link org.springframework.security.context.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor}
* for a class that manages the Authentication per-thread. If you need to have
* threads with different authentication credentials, then you should use the
* Spring Security class instead.
* <p>
* Typically, instances of this class will be automatically created by using
* {@link BasicAuthHttpInvokerProxyFactoryBean}, which will take care of keeping
* this instance updated with the latest authentication token. In case you want
* to use this executor with a different invoker factory, this class implements
* {@link AuthenticationAware} so that it will automatically receive
* notification of changes in the authentication token.
* <p>
* Note that this configuration could lead to instances of this class receiving
* two notifications of an authentication token change. However, that would only
* happen if both the {@link BasicAuthHttpInvokerProxyFactoryBean} is used and
* this class is created as a bean in the application context. This seemed
* unlikely enough that I erred on the side of "ease of use". Also, the current
* implementation does nothing more than store a reference to the new token, so
* receiving two notifications isn't a problem.
*
* @author Larry Streepy
* @see org.springframework.richclient.security.remoting.BasicAuthHttpInvokerProxyFactoryBean
*
*/
public class BasicAuthHttpInvokerRequestExecutor extends
SimpleHttpInvokerRequestExecutor implements
org.valkyriercp.security.AuthenticationAware {
private Authentication authentication;
/**
* Constructor.
*/
public BasicAuthHttpInvokerRequestExecutor() {
}
/*
* (non-Javadoc)
*
* @see org.springframework.richclient.security.AuthenticationAware#
* setAuthenticationToken(org.springframework.security.Authentication)
*/
public void setAuthenticationToken(Authentication authentication) {
this.authentication = authentication;
}
/**
* Get the Authentication object for the current user, if any.
*/
public Authentication getAuthenticationToken() {
return authentication;
}
//
// === SimpleHttpInvokerRequestExecutor methods ===
//
/**
* Provided so subclasses can perform additional configuration if required
* (eg set additional request headers for non-security related information
* etc).
*
* @param con
* the HTTP connection to prepare
* @param contentLength
* the length of the content to send
*
* @throws IOException
* if thrown by HttpURLConnection methods
*/
protected void doPrepareConnection(HttpURLConnection con, int contentLength)
throws IOException {
}
/**
* Called every time a HTTP invocation is made.
* <p>
* Simply allows the parent to setup the connection, and then adds an
* <code>Authorization</code> HTTP header property that will be used for
* BASIC authentication. Following that a call to
* {@link #doPrepareConnection} is made to allow subclasses to apply any
* additional configuration desired to the connection prior to invoking the
* request.
* <p>
* The previously saved authentication token is used to obtain the principal
* and credentials. If the saved token is null, then the "Authorization"
* header will not be added to the request.
*
* @param con
* the HTTP connection to prepare
* @param contentLength
* the length of the content to send
*
* @throws IOException
* if thrown by HttpURLConnection methods
*/
protected void prepareConnection(HttpURLConnection con, int contentLength)
throws IOException {
super.prepareConnection(con, contentLength);
Authentication auth = getAuthenticationToken();
if ((auth != null) && (auth.getName() != null)
&& (auth.getCredentials() != null)) {
String base64 = auth.getName() + ":"
+ auth.getCredentials().toString();
con.setRequestProperty("Authorization", "Basic "
+ new String(Base64.encodeBase64(base64.getBytes())));
if (logger.isDebugEnabled()) {
logger.debug("HttpInvocation now presenting via BASIC authentication with token:: "
+ auth);
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("Unable to set BASIC authentication header as Authentication token is invalid: "
+ auth);
}
}
doPrepareConnection(con, contentLength);
}
}