001 /*
002 * Copyright (c) 2002-2005 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016 package org.springframework.richclient.security.remoting;
017
018 import java.io.IOException;
019 import java.net.HttpURLConnection;
020
021 import org.apache.commons.codec.binary.Base64;
022 import org.springframework.remoting.httpinvoker.SimpleHttpInvokerRequestExecutor;
023 import org.springframework.richclient.security.AuthenticationAware;
024 import org.springframework.security.Authentication;
025
026 /**
027 * Adds BASIC authentication support to <code>SimpleHttpInvokerRequestExecutor</code>.
028 * This class assumes that a single authentication credential will be used for the whole
029 * application (as is typical with a rich client). So, regardless of the thread involved,
030 * it will use the Authentication object obtained from the last authentication token
031 * received.
032 * <p>
033 * In comparison, see
034 * {@link org.springframework.security.context.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor}
035 * for a class that manages the Authentication per-thread. If you need to have threads
036 * with different authentication credentials, then you should use the Spring Security class instead.
037 * <p>
038 * Typically, instances of this class will be automatically created by using
039 * {@link BasicAuthHttpInvokerProxyFactoryBean}, which will take care of keeping this
040 * instance updated with the latest authentication token. In case you want to use this
041 * executor with a different invoker factory, this class implements
042 * {@link AuthenticationAware} so that it will automatically receive notification of
043 * changes in the authentication token.
044 * <p>
045 * Note that this configuration could lead to instances of this class receiving two
046 * notifications of an authentication token change. However, that would only happen
047 * if both the {@link BasicAuthHttpInvokerProxyFactoryBean} is used and this class
048 * is created as a bean in the application context. This seemed unlikely enough
049 * that I erred on the side of "ease of use". Also, the current implementation
050 * does nothing more than store a reference to the new token, so receiving two
051 * notifications isn't a problem.
052 *
053 * @author Larry Streepy
054 * @see org.springframework.richclient.security.remoting.BasicAuthHttpInvokerProxyFactoryBean
055 *
056 */
057 public class BasicAuthHttpInvokerRequestExecutor extends SimpleHttpInvokerRequestExecutor implements
058 AuthenticationAware {
059
060 private Authentication authentication;
061
062 /**
063 * Constructor.
064 */
065 public BasicAuthHttpInvokerRequestExecutor() {
066 }
067
068 /*
069 * (non-Javadoc)
070 * @see org.springframework.richclient.security.AuthenticationAware#setAuthenticationToken(org.springframework.security.Authentication)
071 */
072 public void setAuthenticationToken(Authentication authentication) {
073 this.authentication = authentication;
074 }
075
076 /**
077 * Get the Authentication object for the current user, if any.
078 */
079 public Authentication getAuthenticationToken() {
080 return authentication;
081 }
082
083 //
084 // === SimpleHttpInvokerRequestExecutor methods ===
085 //
086
087 /**
088 * Provided so subclasses can perform additional configuration if required (eg set
089 * additional request headers for non-security related information etc).
090 *
091 * @param con the HTTP connection to prepare
092 * @param contentLength the length of the content to send
093 *
094 * @throws IOException if thrown by HttpURLConnection methods
095 */
096 protected void doPrepareConnection(HttpURLConnection con, int contentLength) throws IOException {
097 }
098
099 /**
100 * Called every time a HTTP invocation is made.
101 * <p>
102 * Simply allows the parent to setup the connection, and then adds an
103 * <code>Authorization</code> HTTP header property that will be used for BASIC
104 * authentication. Following that a call to {@link #doPrepareConnection} is made to
105 * allow subclasses to apply any additional configuration desired to the connection
106 * prior to invoking the request.
107 * <p>
108 * The previously saved authentication token is used to obtain the principal and
109 * credentials. If the saved token is null, then the "Authorization" header will not
110 * be added to the request.
111 *
112 * @param con the HTTP connection to prepare
113 * @param contentLength the length of the content to send
114 *
115 * @throws IOException if thrown by HttpURLConnection methods
116 */
117 protected void prepareConnection(HttpURLConnection con, int contentLength) throws IOException {
118
119 super.prepareConnection( con, contentLength );
120
121 Authentication auth = getAuthenticationToken();
122
123 if( (auth != null) && (auth.getName() != null) && (auth.getCredentials() != null) ) {
124 String base64 = auth.getName() + ":" + auth.getCredentials().toString();
125 con.setRequestProperty( "Authorization", "Basic " + new String( Base64.encodeBase64( base64.getBytes() ) ) );
126
127 if( logger.isDebugEnabled() ) {
128 logger.debug( "HttpInvocation now presenting via BASIC authentication with token:: " + auth );
129 }
130 } else {
131 if( logger.isDebugEnabled() ) {
132 logger.debug( "Unable to set BASIC authentication header as Authentication token is invalid: " + auth );
133 }
134 }
135
136 doPrepareConnection( con, contentLength );
137 }
138
139 }