diff --git a/core/src/main/java/org/acegisecurity/ui/basicauth/BasicProcessingFilter.java b/core/src/main/java/org/acegisecurity/ui/basicauth/BasicProcessingFilter.java index fe1a55d00d..41aab01b39 100644 --- a/core/src/main/java/org/acegisecurity/ui/basicauth/BasicProcessingFilter.java +++ b/core/src/main/java/org/acegisecurity/ui/basicauth/BasicProcessingFilter.java @@ -12,6 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package net.sf.acegisecurity.ui.basicauth; import net.sf.acegisecurity.Authentication; @@ -45,13 +46,13 @@ import javax.servlet.http.HttpServletResponse; /** * Processes a HTTP request's BASIC authorization headers, putting the result * into the SecurityContextHolder. - * + * *

* For a detailed background on what this filter is designed to process, refer * to RFC 1945, Section * 11.1. Any realm name presented in the HTTP request is ignored. *

- * + * *

* In summary, this filter is responsible for processing any request that has a * HTTP request header of Authorization with an authentication @@ -60,28 +61,28 @@ import javax.servlet.http.HttpServletResponse; * "Aladdin" with password "open sesame" the following header would be * presented: *

- * + * *

* Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==. *

- * + * *

* This filter can be used to provide BASIC authentication services to both * remoting protocol clients (such as Hessian and SOAP) as well as standard * user agents (such as Internet Explorer and Netscape). *

- * + * *

* If authentication is successful, the resulting {@link Authentication} object * will be placed into the SecurityContextHolder. *

- * + * *

* If authentication fails, an {@link AuthenticationEntryPoint} implementation * is called. Usually this should be {@link BasicProcessingFilterEntryPoint}, * which will prompt the user to authenticate again via BASIC authentication. *

- * + * *

* Basic authentication is an attractive protocol because it is simple and * widely deployed. However, it still transmits a password in clear text and @@ -90,7 +91,7 @@ import javax.servlet.http.HttpServletResponse; * authentication wherever possible. See {@link * net.sf.acegisecurity.ui.digestauth.DigestProcessingFilter}. *

- * + * *

* Do not use this class directly. Instead configure * web.xml to use the {@link @@ -101,10 +102,17 @@ import javax.servlet.http.HttpServletResponse; * @version $Id$ */ public class BasicProcessingFilter implements Filter, InitializingBean { + //~ Static fields/initializers ============================================= + private static final Log logger = LogFactory.getLog(BasicProcessingFilter.class); + + //~ Instance fields ======================================================== + private AuthenticationEntryPoint authenticationEntryPoint; private AuthenticationManager authenticationManager; + //~ Methods ================================================================ + public void setAuthenticationEntryPoint( AuthenticationEntryPoint authenticationEntryPoint) { this.authenticationEntryPoint = authenticationEntryPoint; @@ -130,8 +138,7 @@ public class BasicProcessingFilter implements Filter, InitializingBean { "An AuthenticationEntryPoint is required"); } - public void destroy() { - } + public void destroy() {} public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { @@ -165,40 +172,47 @@ public class BasicProcessingFilter implements Filter, InitializingBean { password = token.substring(delim + 1); } - UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, - password); - authRequest.setDetails(new WebAuthenticationDetails(httpRequest, - false)); + // Only reauthenticate if username doesn't match ContextHolder and user isn't authenticated (see SEC-53) + Authentication existingAuth = SecurityContextHolder.getContext() + .getAuthentication(); - Authentication authResult; + if ((existingAuth == null) + || !existingAuth.getName().equals(username) + || !existingAuth.isAuthenticated()) { + UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, + password); + authRequest.setDetails(new WebAuthenticationDetails( + httpRequest, false)); - try { - authResult = authenticationManager.authenticate(authRequest); - } catch (AuthenticationException failed) { - // Authentication failed - if (logger.isDebugEnabled()) { - logger.debug("Authentication request for user: " + - username + " failed: " + failed.toString()); + Authentication authResult; + + try { + authResult = authenticationManager.authenticate(authRequest); + } catch (AuthenticationException failed) { + // Authentication failed + if (logger.isDebugEnabled()) { + logger.debug("Authentication request for user: " + + username + " failed: " + failed.toString()); + } + + SecurityContextHolder.getContext().setAuthentication(null); + authenticationEntryPoint.commence(request, response, failed); + + return; } - SecurityContextHolder.getContext().setAuthentication(null); - authenticationEntryPoint.commence(request, response, failed); + // Authentication success + if (logger.isDebugEnabled()) { + logger.debug("Authentication success: " + + authResult.toString()); + } - return; + SecurityContextHolder.getContext().setAuthentication(authResult); } - - // Authentication success - if (logger.isDebugEnabled()) { - logger.debug("Authentication success: " + - authResult.toString()); - } - - SecurityContextHolder.getContext().setAuthentication(authResult); } chain.doFilter(request, response); } - public void init(FilterConfig arg0) throws ServletException { - } + public void init(FilterConfig arg0) throws ServletException {} } diff --git a/core/src/test/java/org/acegisecurity/ui/basicauth/BasicProcessingFilterTests.java b/core/src/test/java/org/acegisecurity/ui/basicauth/BasicProcessingFilterTests.java index 0a82f855a3..b27af1846c 100644 --- a/core/src/test/java/org/acegisecurity/ui/basicauth/BasicProcessingFilterTests.java +++ b/core/src/test/java/org/acegisecurity/ui/basicauth/BasicProcessingFilterTests.java @@ -183,6 +183,7 @@ public class BasicProcessingFilterTests extends TestCase { MockHttpServletResponse response = new MockHttpServletResponse(); // Test + assertNull(SecurityContextHolder.getContext().getAuthentication()); executeFilterInContainerSimulator(config, filter, request, response, chain); @@ -280,7 +281,7 @@ public class BasicProcessingFilterTests extends TestCase { // NOW PERFORM FAILED AUTHENTICATION // Setup our HTTP request - token = "marissa:WRONG_PASSWORD"; + token = "otherUser:WRONG_PASSWORD"; request = new MockHttpServletRequest(); request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes())));