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 }