Browse Source

SEC-1167: Introduce more flexible SavedRequest handling. Introduced interface for SavedRequest.

Luke Taylor 16 năm trước cách đây
mục cha
commit
4064b7b4f6
15 tập tin đã thay đổi với 442 bổ sung394 xóa
  1. 1 1
      web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java
  2. 10 9
      web/src/main/java/org/springframework/security/web/authentication/SavedRequestAwareAuthenticationSuccessHandler.java
  3. 341 0
      web/src/main/java/org/springframework/security/web/savedrequest/DefaultSavedRequest.java
  4. 11 9
      web/src/main/java/org/springframework/security/web/savedrequest/HttpSessionRequestCache.java
  5. 2 1
      web/src/main/java/org/springframework/security/web/savedrequest/RequestCache.java
  6. 17 345
      web/src/main/java/org/springframework/security/web/savedrequest/SavedRequest.java
  7. 3 3
      web/src/main/java/org/springframework/security/web/savedrequest/SavedRequestAwareWrapper.java
  8. 2 2
      web/src/main/java/org/springframework/security/web/session/DefaultSessionAuthenticationStrategy.java
  9. 4 4
      web/src/test/java/org/springframework/security/web/access/ExceptionTranslationFilterTests.java
  10. 8 8
      web/src/test/java/org/springframework/security/web/authentication/AbstractProcessingFilterTests.java
  11. 33 0
      web/src/test/java/org/springframework/security/web/savedrequest/HttpSessionRequestCacheTests.java
  12. 2 4
      web/src/test/java/org/springframework/security/web/savedrequest/RequestCacheAwareFilterTests.java
  13. 2 2
      web/src/test/java/org/springframework/security/web/savedrequest/SavedRequestAwareWrapperTests.java
  14. 3 3
      web/src/test/java/org/springframework/security/web/savedrequest/SavedRequestTests.java
  15. 3 3
      web/src/test/java/org/springframework/security/web/session/DefaultSessionAuthenticationStrategyTests.java

+ 1 - 1
web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java

@@ -67,7 +67,7 @@ import org.springframework.web.filter.GenericFilterBean;
  * The configured {@link #setAuthenticationSuccessHandler(AuthenticationSuccessHandler) AuthenticationSuccessHandler} will
  * then be called to take the redirect to the appropriate destination after a successful login. The default behaviour
  * is implemented in a {@link SavedRequestAwareAuthenticationSuccessHandler} which will make use of any
- * <tt>SavedRequest</tt> set by the <tt>ExceptionTranslationFilter</tt> and redirect the user to the URL contained
+ * <tt>DefaultSavedRequest</tt> set by the <tt>ExceptionTranslationFilter</tt> and redirect the user to the URL contained
  * therein. Otherwise it will redirect to the webapp root "/". You can customize this behaviour by injecting a
  * differently configured instance of this class, or by using a different implementation.
  * <p>

+ 10 - 9
web/src/main/java/org/springframework/security/web/authentication/SavedRequestAwareAuthenticationSuccessHandler.java

@@ -13,10 +13,11 @@ import org.springframework.security.web.access.ExceptionTranslationFilter;
 import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
 import org.springframework.security.web.savedrequest.RequestCache;
 import org.springframework.security.web.savedrequest.SavedRequest;
+import org.springframework.security.web.savedrequest.DefaultSavedRequest;
 import org.springframework.util.StringUtils;
 
 /**
- * An authentication success strategy which can make use of the {@link SavedRequest} which may have been stored in
+ * An authentication success strategy which can make use of the {@link DefaultSavedRequest} which may have been stored in
  * the session by the {@link ExceptionTranslationFilter}. When such a request is intercepted and requires authentication,
  * the request data is stored to record the original destination before the authentication process commenced, and to
  * allow the request to be reconstructed when a redirect to the same URL occurs. This class is responsible for
@@ -26,21 +27,21 @@ import org.springframework.util.StringUtils;
  * <ul>
  * <li>
  * If the <tt>alwaysUseDefaultTargetUrl</tt> property is set to true, the <tt>defaultTargetUrl</tt>
- * will be used for the destination. Any <tt>SavedRequest</tt> stored in the session will be
+ * will be used for the destination. Any <tt>DefaultSavedRequest</tt> stored in the session will be
  * removed.
  * </li>
  * <li>
  * If the <tt>targetUrlParameter</tt> has been set on the request, the value will be used as the destination.
- * Any <tt>SavedRequest</tt> will again be removed.
+ * Any <tt>DefaultSavedRequest</tt> will again be removed.
  * </li>
  * <li>
- * If a {@link SavedRequest} is found in the <tt>RequestCache</tt> (as set by the {@link ExceptionTranslationFilter} to
+ * If a {@link DefaultSavedRequest} is found in the <tt>RequestCache</tt> (as set by the {@link ExceptionTranslationFilter} to
  * record the original destination before the authentication process commenced), a redirect will be performed to the
- * Url of that original destination. The <tt>SavedRequest</tt> object will remain cached and be picked up
+ * Url of that original destination. The <tt>DefaultSavedRequest</tt> object will remain cached and be picked up
  * when the redirected request is received (See {@link SavedRequestAwareWrapper}).
  * </li>
  * <li>
- * If no <tt>SavedRequest</tt> is found, it will delegate to the base class.
+ * If no <tt>DefaultSavedRequest</tt> is found, it will delegate to the base class.
  * </li>
  * </ul>
  *
@@ -72,9 +73,9 @@ public class SavedRequestAwareAuthenticationSuccessHandler extends SimpleUrlAuth
             return;
         }
 
-        // Use the SavedRequest URL
-        String targetUrl = savedRequest.getFullRequestUrl();
-        logger.debug("Redirecting to SavedRequest Url: " + targetUrl);
+        // Use the DefaultSavedRequest URL
+        String targetUrl = savedRequest.getRedirectUrl();
+        logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);
         getRedirectStrategy().sendRedirect(request, response, targetUrl);
     }
 

+ 341 - 0
web/src/main/java/org/springframework/security/web/savedrequest/DefaultSavedRequest.java

@@ -0,0 +1,341 @@
+/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
+ *
+ * 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.springframework.security.web.savedrequest;
+
+import org.springframework.security.web.PortResolver;
+import org.springframework.security.web.util.UrlUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.util.Assert;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TreeMap;
+
+
+/**
+ * Represents central information from a <code>HttpServletRequest</code>.<p>This class is used by {@link
+ * org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter} and {@link org.springframework.security.web.savedrequest.SavedRequestAwareWrapper} to
+ * reproduce the request after successful authentication. An instance of this class is stored at the time of an
+ * authentication exception by {@link org.springframework.security.web.access.ExceptionTranslationFilter}.</p>
+ * <p><em>IMPLEMENTATION NOTE</em>: It is assumed that this object is accessed only from the context of a single
+ * thread, so no synchronization around internal collection classes is performed.</p>
+ * <p>This class is based on code in Apache Tomcat.</p>
+ *
+ * @author Craig McClanahan
+ * @author Andrey Grebnev
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class DefaultSavedRequest implements SavedRequest {
+    //~ Static fields/initializers =====================================================================================
+
+    protected static final Log logger = LogFactory.getLog(DefaultSavedRequest.class);
+
+    public static final String SPRING_SECURITY_SAVED_REQUEST_KEY = "SPRING_SECURITY_SAVED_REQUEST_KEY";
+
+    //~ Instance fields ================================================================================================
+
+    private ArrayList<SavedCookie> cookies = new ArrayList<SavedCookie>();
+    private ArrayList<Locale> locales = new ArrayList<Locale>();
+    private Map<String, List<String>> headers = new TreeMap<String, List<String>>(String.CASE_INSENSITIVE_ORDER);
+    private Map<String, String[]> parameters = new TreeMap<String, String[]>(String.CASE_INSENSITIVE_ORDER);
+    private String contextPath;
+    private String method;
+    private String pathInfo;
+    private String queryString;
+    private String requestURI;
+    private String requestURL;
+    private String scheme;
+    private String serverName;
+    private String servletPath;
+    private int serverPort;
+
+    //~ Constructors ===================================================================================================
+
+    @SuppressWarnings("unchecked")
+    public DefaultSavedRequest(HttpServletRequest request, PortResolver portResolver) {
+        Assert.notNull(request, "Request required");
+        Assert.notNull(portResolver, "PortResolver required");
+
+        // Cookies
+        Cookie[] cookies = request.getCookies();
+
+        if (cookies != null) {
+            for (int i = 0; i < cookies.length; i++) {
+                this.addCookie(cookies[i]);
+            }
+        }
+
+        // Headers
+        Enumeration<String> names = request.getHeaderNames();
+
+        while (names.hasMoreElements()) {
+            String name = names.nextElement();
+            Enumeration<String> values = request.getHeaders(name);
+
+            while (values.hasMoreElements()) {
+                this.addHeader(name, values.nextElement());
+            }
+        }
+
+        // Locales
+        Enumeration<Locale> locales = request.getLocales();
+
+        while (locales.hasMoreElements()) {
+            Locale locale = (Locale) locales.nextElement();
+            this.addLocale(locale);
+        }
+
+        // Parameters
+        Map<String,Object> parameters = request.getParameterMap();
+
+        for(String paramName : parameters.keySet()) {
+            Object paramValues = parameters.get(paramName);
+            if (paramValues instanceof String[]) {
+                this.addParameter(paramName, (String[]) paramValues);
+            } else {
+                if (logger.isWarnEnabled()) {
+                    logger.warn("ServletRequest.getParameterMap() returned non-String array");
+                }
+            }
+        }
+
+        // Primitives
+        this.method = request.getMethod();
+        this.pathInfo = request.getPathInfo();
+        this.queryString = request.getQueryString();
+        this.requestURI = request.getRequestURI();
+        this.serverPort = portResolver.getServerPort(request);
+        this.requestURL = request.getRequestURL().toString();
+        this.scheme = request.getScheme();
+        this.serverName = request.getServerName();
+        this.contextPath = request.getContextPath();
+        this.servletPath = request.getServletPath();
+    }
+
+    //~ Methods ========================================================================================================
+
+    private void addCookie(Cookie cookie) {
+        cookies.add(new SavedCookie(cookie));
+    }
+
+    private void addHeader(String name, String value) {
+        List<String> values = headers.get(name);
+
+        if (values == null) {
+            values = new ArrayList<String>();
+            headers.put(name, values);
+        }
+
+        values.add(value);
+    }
+
+    private void addLocale(Locale locale) {
+        locales.add(locale);
+    }
+
+    private void addParameter(String name, String[] values) {
+        parameters.put(name, values);
+    }
+
+    /**
+     * Determines if the current request matches the <code>DefaultSavedRequest</code>.
+     * <p>
+     * All URL arguments are considered but not cookies, locales, headers or parameters.
+     * <p>
+     *
+     */
+    public boolean doesRequestMatch(HttpServletRequest request, PortResolver portResolver) {
+
+        if (!propertyEquals("pathInfo", this.pathInfo, request.getPathInfo())) {
+            return false;
+        }
+
+        if (!propertyEquals("queryString", this.queryString, request.getQueryString())) {
+            return false;
+        }
+
+        if (!propertyEquals("requestURI", this.requestURI, request.getRequestURI())) {
+            return false;
+        }
+
+        if (!"GET".equals(request.getMethod()) && "GET".equals(method)) {
+            // A save GET should not match an incoming non-GET method
+            return false;
+        }
+
+        if (!propertyEquals("serverPort", new Integer(this.serverPort), new Integer(portResolver.getServerPort(request))))
+        {
+            return false;
+        }
+
+        if (!propertyEquals("requestURL", this.requestURL, request.getRequestURL().toString())) {
+            return false;
+        }
+
+        if (!propertyEquals("scheme", this.scheme, request.getScheme())) {
+            return false;
+        }
+
+        if (!propertyEquals("serverName", this.serverName, request.getServerName())) {
+            return false;
+        }
+
+        if (!propertyEquals("contextPath", this.contextPath, request.getContextPath())) {
+            return false;
+        }
+
+        if (!propertyEquals("servletPath", this.servletPath, request.getServletPath())) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public String getContextPath() {
+        return contextPath;
+    }
+
+    public List<Cookie> getCookies() {
+        List<Cookie> cookieList = new ArrayList<Cookie>(cookies.size());
+
+        for (SavedCookie savedCookie : cookies) {
+            cookieList.add(savedCookie.getCookie());
+        }
+
+        return cookieList;
+    }
+
+    /**
+     * Indicates the URL that the user agent used for this request.
+     *
+     * @return the full URL of this request
+     */
+    public String getRedirectUrl() {
+        return UrlUtils.buildFullRequestUrl(scheme, serverName, serverPort, contextPath, servletPath, requestURI,
+                pathInfo, queryString);
+    }
+
+    public Iterator<String> getHeaderNames() {
+        return (headers.keySet().iterator());
+    }
+
+    public Iterator<String> getHeaderValues(String name) {
+        List<String> values = headers.get(name);
+
+        if (values == null) {
+            values = Collections.emptyList();
+        }
+
+        return (values.iterator());
+    }
+
+    public Iterator<Locale> getLocales() {
+        return (locales.iterator());
+    }
+
+    public String getMethod() {
+        return method;
+    }
+
+    public Map<String, String[]> getParameterMap() {
+        return parameters;
+    }
+
+    public Iterator<String> getParameterNames() {
+        return (parameters.keySet().iterator());
+    }
+
+    public String[] getParameterValues(String name) {
+        return ((String[]) parameters.get(name));
+    }
+
+    public String getPathInfo() {
+        return pathInfo;
+    }
+
+    public String getQueryString() {
+        return (this.queryString);
+    }
+
+    public String getRequestURI() {
+        return (this.requestURI);
+    }
+
+    public String getRequestURL() {
+        return requestURL;
+    }
+
+    public String getScheme() {
+        return scheme;
+    }
+
+    public String getServerName() {
+        return serverName;
+    }
+
+    public int getServerPort() {
+        return serverPort;
+    }
+
+    public String getServletPath() {
+        return servletPath;
+    }
+
+    private boolean propertyEquals(String log, Object arg1, Object arg2) {
+        if ((arg1 == null) && (arg2 == null)) {
+            if (logger.isDebugEnabled()) {
+                logger.debug(log + ": both null (property equals)");
+            }
+
+            return true;
+        }
+
+        if (((arg1 == null) && (arg2 != null)) || ((arg1 != null) && (arg2 == null))) {
+            if (logger.isDebugEnabled()) {
+                logger.debug(log + ": arg1=" + arg1 + "; arg2=" + arg2 + " (property not equals)");
+            }
+
+            return false;
+        }
+
+        if (arg1.equals(arg2)) {
+            if (logger.isDebugEnabled()) {
+                logger.debug(log + ": arg1=" + arg1 + "; arg2=" + arg2 + " (property equals)");
+            }
+
+            return true;
+        } else {
+            if (logger.isDebugEnabled()) {
+                logger.debug(log + ": arg1=" + arg1 + "; arg2=" + arg2 + " (property not equals)");
+            }
+
+            return false;
+        }
+    }
+
+    public String toString() {
+        return "DefaultSavedRequest[" + getRedirectUrl() + "]";
+    }
+}

+ 11 - 9
web/src/main/java/org/springframework/security/web/savedrequest/HttpSessionRequestCache.java

@@ -10,7 +10,9 @@ import org.springframework.security.web.PortResolver;
 import org.springframework.security.web.PortResolverImpl;
 
 /**
- * <tt>RequestCache</tt> which stores the SavedRequest in the HttpSession.
+ * <tt>RequestCache</tt> which stores the <tt>SavedRequest</tt> in the HttpSession.
+ *
+ * The {@link DefaultSavedRequest} class is used as the implementation.
  *
  * @author Luke Taylor
  * @version $Id$
@@ -28,13 +30,13 @@ public class HttpSessionRequestCache implements RequestCache {
      */
     public void saveRequest(HttpServletRequest request, HttpServletResponse response) {
         if (!justUseSavedRequestOnGet || "GET".equals(request.getMethod())) {
-            SavedRequest savedRequest = new SavedRequest(request, portResolver);
+            DefaultSavedRequest savedRequest = new DefaultSavedRequest(request, portResolver);
 
             if (createSessionAllowed || request.getSession(false) != null) {
                 // Store the HTTP request itself. Used by AbstractAuthenticationProcessingFilter
                 // for redirection after successful authentication (SEC-29)
-                request.getSession().setAttribute(SavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY, savedRequest);
-                logger.debug("SavedRequest added to Session: " + savedRequest);
+                request.getSession().setAttribute(DefaultSavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY, savedRequest);
+                logger.debug("DefaultSavedRequest added to Session: " + savedRequest);
             }
         }
 
@@ -44,7 +46,7 @@ public class HttpSessionRequestCache implements RequestCache {
         HttpSession session = currentRequest.getSession(false);
 
         if (session != null) {
-            return (SavedRequest) session.getAttribute(SavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY);
+            return (DefaultSavedRequest) session.getAttribute(DefaultSavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY);
         }
 
         return null;
@@ -54,13 +56,13 @@ public class HttpSessionRequestCache implements RequestCache {
         HttpSession session = currentRequest.getSession(false);
 
         if (session != null) {
-            logger.debug("Removing SavedRequest from session if present");
-            session.removeAttribute(SavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY);
+            logger.debug("Removing DefaultSavedRequest from session if present");
+            session.removeAttribute(DefaultSavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY);
         }
     }
 
     public HttpServletRequest getMatchingRequest(HttpServletRequest request, HttpServletResponse response) {
-        SavedRequest saved = getRequest(request, response);
+        DefaultSavedRequest saved = (DefaultSavedRequest) getRequest(request, response);
 
         if (saved == null) {
             return null;
@@ -77,7 +79,7 @@ public class HttpSessionRequestCache implements RequestCache {
     }
 
     /**
-     * If <code>true</code>, will only use <code>SavedRequest</code> to determine the target URL on successful
+     * If <code>true</code>, will only use <code>DefaultSavedRequest</code> to determine the target URL on successful
      * authentication if the request that caused the authentication request was a GET. Defaults to false.
      */
     public void setJustUseSavedRequestOnGet(boolean justUseSavedRequestOnGet) {

+ 2 - 1
web/src/main/java/org/springframework/security/web/savedrequest/RequestCache.java

@@ -34,7 +34,8 @@ public interface RequestCache {
      *
      * @param request
      * @param response
-     * @return the wrapped save request, if it matches the
+     * @return the wrapped save request, if it matches the original, or null if there is no cached request or it doesn't
+     *    match.
      */
     HttpServletRequest getMatchingRequest(HttpServletRequest request, HttpServletResponse response);
 

+ 17 - 345
web/src/main/java/org/springframework/security/web/savedrequest/SavedRequest.java

@@ -1,345 +1,17 @@
-/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
- *
- * 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.springframework.security.web.savedrequest;
-
-import org.springframework.security.web.PortResolver;
-import org.springframework.security.web.util.UrlUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.util.Assert;
-
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.TreeMap;
-
-
-/**
- * Represents central information from a <code>HttpServletRequest</code>.<p>This class is used by {@link
- * org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter} and {@link org.springframework.security.web.savedrequest.SavedRequestAwareWrapper} to
- * reproduce the request after successful authentication. An instance of this class is stored at the time of an
- * authentication exception by {@link org.springframework.security.web.access.ExceptionTranslationFilter}.</p>
- * <p><em>IMPLEMENTATION NOTE</em>: It is assumed that this object is accessed only from the context of a single
- * thread, so no synchronization around internal collection classes is performed.</p>
- * <p>This class is based on code in Apache Tomcat.</p>
- *
- * @author Craig McClanahan
- * @author Andrey Grebnev
- * @author Ben Alex
- * @version $Id$
- */
-public class SavedRequest implements java.io.Serializable {
-    //~ Static fields/initializers =====================================================================================
-
-    protected static final Log logger = LogFactory.getLog(SavedRequest.class);
-
-    public static final String SPRING_SECURITY_SAVED_REQUEST_KEY = "SPRING_SECURITY_SAVED_REQUEST_KEY";
-
-    //~ Instance fields ================================================================================================
-
-    private ArrayList<SavedCookie> cookies = new ArrayList<SavedCookie>();
-    private ArrayList<Locale> locales = new ArrayList<Locale>();
-    private Map<String, List<String>> headers = new TreeMap<String, List<String>>(String.CASE_INSENSITIVE_ORDER);
-    private Map<String, String[]> parameters = new TreeMap<String, String[]>(String.CASE_INSENSITIVE_ORDER);
-    private String contextPath;
-    private String method;
-    private String pathInfo;
-    private String queryString;
-    private String requestURI;
-    private String requestURL;
-    private String scheme;
-    private String serverName;
-    private String servletPath;
-    private int serverPort;
-
-    //~ Constructors ===================================================================================================
-
-    @SuppressWarnings("unchecked")
-    public SavedRequest(HttpServletRequest request, PortResolver portResolver) {
-        Assert.notNull(request, "Request required");
-        Assert.notNull(portResolver, "PortResolver required");
-
-        // Cookies
-        Cookie[] cookies = request.getCookies();
-
-        if (cookies != null) {
-            for (int i = 0; i < cookies.length; i++) {
-                this.addCookie(cookies[i]);
-            }
-        }
-
-        // Headers
-        Enumeration<String> names = request.getHeaderNames();
-
-        while (names.hasMoreElements()) {
-            String name = names.nextElement();
-            Enumeration<String> values = request.getHeaders(name);
-
-            while (values.hasMoreElements()) {
-                this.addHeader(name, values.nextElement());
-            }
-        }
-
-        // Locales
-        Enumeration<Locale> locales = request.getLocales();
-
-        while (locales.hasMoreElements()) {
-            Locale locale = (Locale) locales.nextElement();
-            this.addLocale(locale);
-        }
-
-        // Parameters
-        Map<String,Object> parameters = request.getParameterMap();
-
-        for(String paramName : parameters.keySet()) {
-            Object paramValues = parameters.get(paramName);
-            if (paramValues instanceof String[]) {
-                this.addParameter(paramName, (String[]) paramValues);
-            } else {
-                if (logger.isWarnEnabled()) {
-                    logger.warn("ServletRequest.getParameterMap() returned non-String array");
-                }
-            }
-        }
-
-        // Primitives
-        this.method = request.getMethod();
-        this.pathInfo = request.getPathInfo();
-        this.queryString = request.getQueryString();
-        this.requestURI = request.getRequestURI();
-        this.serverPort = portResolver.getServerPort(request);
-        this.requestURL = request.getRequestURL().toString();
-        this.scheme = request.getScheme();
-        this.serverName = request.getServerName();
-        this.contextPath = request.getContextPath();
-        this.servletPath = request.getServletPath();
-    }
-
-    //~ Methods ========================================================================================================
-
-    private void addCookie(Cookie cookie) {
-        cookies.add(new SavedCookie(cookie));
-    }
-
-    private void addHeader(String name, String value) {
-        List<String> values = headers.get(name);
-
-        if (values == null) {
-            values = new ArrayList<String>();
-            headers.put(name, values);
-        }
-
-        values.add(value);
-    }
-
-    private void addLocale(Locale locale) {
-        locales.add(locale);
-    }
-
-    private void addParameter(String name, String[] values) {
-        parameters.put(name, values);
-    }
-
-    /**
-     * Determines if the current request matches the <code>SavedRequest</code>. All URL arguments are
-     * considered, but <em>not</em> method (POST/GET), cookies, locales, headers or parameters.
-     */
-    public boolean doesRequestMatch(HttpServletRequest request, PortResolver portResolver) {
-        Assert.notNull(request, "Request required");
-        Assert.notNull(portResolver, "PortResolver required");
-
-        if (!propertyEquals("pathInfo", this.pathInfo, request.getPathInfo())) {
-            return false;
-        }
-
-        if (!propertyEquals("queryString", this.queryString, request.getQueryString())) {
-            return false;
-        }
-
-        if (!propertyEquals("requestURI", this.requestURI, request.getRequestURI())) {
-            return false;
-        }
-
-        if (!propertyEquals("serverPort", new Integer(this.serverPort), new Integer(portResolver.getServerPort(request))))
-        {
-            return false;
-        }
-
-        if (!propertyEquals("requestURL", this.requestURL, request.getRequestURL().toString())) {
-            return false;
-        }
-
-        if (!propertyEquals("scheme", this.scheme, request.getScheme())) {
-            return false;
-        }
-
-        if (!propertyEquals("serverName", this.serverName, request.getServerName())) {
-            return false;
-        }
-
-        if (!propertyEquals("contextPath", this.contextPath, request.getContextPath())) {
-            return false;
-        }
-
-        if (!propertyEquals("servletPath", this.servletPath, request.getServletPath())) {
-            return false;
-        }
-
-        return true;
-    }
-
-    public String getContextPath() {
-        return contextPath;
-    }
-
-    public List<Cookie> getCookies() {
-        List<Cookie> cookieList = new ArrayList<Cookie>(cookies.size());
-
-        for (SavedCookie savedCookie : cookies) {
-            cookieList.add(savedCookie.getCookie());
-        }
-
-        return cookieList;
-    }
-
-    /**
-     * Indicates the URL that the user agent used for this request.
-     *
-     * @return the full URL of this request
-     */
-    public String getFullRequestUrl() {
-        return UrlUtils.buildFullRequestUrl(this.getScheme(), this.getServerName(), this.getServerPort(), this.getContextPath(),
-        this.getServletPath(), this.getRequestURI(), this.getPathInfo(), this.getQueryString());
-    }
-
-    public Iterator<String> getHeaderNames() {
-        return (headers.keySet().iterator());
-    }
-
-    public Iterator<String> getHeaderValues(String name) {
-        List<String> values = headers.get(name);
-
-        if (values == null) {
-            values = Collections.emptyList();
-        }
-
-        return (values.iterator());
-    }
-
-    public Iterator<Locale> getLocales() {
-        return (locales.iterator());
-    }
-
-    public String getMethod() {
-        return method;
-    }
-
-    public Map<String, String[]> getParameterMap() {
-        return parameters;
-    }
-
-    public Iterator<String> getParameterNames() {
-        return (parameters.keySet().iterator());
-    }
-
-    public String[] getParameterValues(String name) {
-        return ((String[]) parameters.get(name));
-    }
-
-    public String getPathInfo() {
-        return pathInfo;
-    }
-
-    public String getQueryString() {
-        return (this.queryString);
-    }
-
-    public String getRequestURI() {
-        return (this.requestURI);
-    }
-
-    public String getRequestURL() {
-        return requestURL;
-    }
-
-    /**
-     * Obtains the web application-specific fragment of the URL.
-     *
-     * @return the URL, excluding any server name, context path or servlet path
-     */
-    public String getRequestUrl() {
-        return UrlUtils.buildRequestUrl(this.getServletPath(), this.getRequestURI(), this.getContextPath(), this.getPathInfo(),
-        this.getQueryString());
-    }
-
-    public String getScheme() {
-        return scheme;
-    }
-
-    public String getServerName() {
-        return serverName;
-    }
-
-    public int getServerPort() {
-        return serverPort;
-    }
-
-    public String getServletPath() {
-        return servletPath;
-    }
-
-    private boolean propertyEquals(String log, Object arg1, Object arg2) {
-        if ((arg1 == null) && (arg2 == null)) {
-            if (logger.isDebugEnabled()) {
-                logger.debug(log + ": both null (property equals)");
-            }
-
-            return true;
-        }
-
-        if (((arg1 == null) && (arg2 != null)) || ((arg1 != null) && (arg2 == null))) {
-            if (logger.isDebugEnabled()) {
-                logger.debug(log + ": arg1=" + arg1 + "; arg2=" + arg2 + " (property not equals)");
-            }
-
-            return false;
-        }
-
-        if (arg1.equals(arg2)) {
-            if (logger.isDebugEnabled()) {
-                logger.debug(log + ": arg1=" + arg1 + "; arg2=" + arg2 + " (property equals)");
-            }
-
-            return true;
-        } else {
-            if (logger.isDebugEnabled()) {
-                logger.debug(log + ": arg1=" + arg1 + "; arg2=" + arg2 + " (property not equals)");
-            }
-
-            return false;
-        }
-    }
-
-    public String toString() {
-        return "SavedRequest[" + getFullRequestUrl() + "]";
-    }
-}
+package org.springframework.security.web.savedrequest;
+
+/**
+ * Encapsulates the functionality required of a cached request, in order for an authentication mechanism (typically
+ * form-based login) to redirect to the original URL.
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ * @since 3.0
+ */
+public interface SavedRequest extends java.io.Serializable {
+
+    /**
+     * @return the URL for the saved request, allowing a redirect to be performed.
+     */
+    String getRedirectUrl();
+}

+ 3 - 3
web/src/main/java/org/springframework/security/web/savedrequest/SavedRequestAwareWrapper.java

@@ -65,7 +65,7 @@ class SavedRequestAwareWrapper extends HttpServletRequestWrapper {
 
     //~ Instance fields ================================================================================================
 
-    protected SavedRequest savedRequest = null;
+    protected DefaultSavedRequest savedRequest = null;
 
     /**
      * The set of SimpleDateFormat formats to use in getDateHeader(). Notice that because SimpleDateFormat is
@@ -75,7 +75,7 @@ class SavedRequestAwareWrapper extends HttpServletRequestWrapper {
 
     //~ Constructors ===================================================================================================
 
-    public SavedRequestAwareWrapper(SavedRequest saved, HttpServletRequest request) {
+    public SavedRequestAwareWrapper(DefaultSavedRequest saved, HttpServletRequest request) {
         super(request);
         savedRequest = saved;
 
@@ -234,7 +234,7 @@ class SavedRequestAwareWrapper extends HttpServletRequestWrapper {
      * In both cases the value from the wrapped request should be used.
      * <p>
      * If the value from the wrapped request is null, an attempt will be made to retrieve the parameter
-     * from the SavedRequest, if available..
+     * from the DefaultSavedRequest, if available..
      */
     @Override
     public String getParameter(String name) {

+ 2 - 2
web/src/main/java/org/springframework/security/web/session/DefaultSessionAuthenticationStrategy.java

@@ -13,7 +13,7 @@ import javax.servlet.http.HttpSession;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.springframework.security.core.Authentication;
-import org.springframework.security.web.savedrequest.SavedRequest;
+import org.springframework.security.web.savedrequest.DefaultSavedRequest;
 
 /**
  * The default implementation of {@link SessionAuthenticationStrategy}.
@@ -45,7 +45,7 @@ public class DefaultSessionAuthenticationStrategy implements SessionAuthenticati
      * In the case where the attributes will not be migrated, this field allows a list of named attributes
      * which should <em>not</em> be discarded.
      */
-    private List<String> retainedAttributes = Arrays.asList(SavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY);
+    private List<String> retainedAttributes = Arrays.asList(DefaultSavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY);
 
     /**
      * If set to <tt>true</tt>, a session will always be created, even if one didn't exist at the start of the request.

+ 4 - 4
web/src/test/java/org/springframework/security/web/access/ExceptionTranslationFilterTests.java

@@ -42,7 +42,7 @@ import org.springframework.security.core.authority.AuthorityUtils;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.web.AuthenticationEntryPoint;
 import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
-import org.springframework.security.web.savedrequest.SavedRequest;
+import org.springframework.security.web.savedrequest.DefaultSavedRequest;
 import org.springframework.security.web.util.ThrowableAnalyzer;
 
 /**
@@ -66,9 +66,9 @@ public class ExceptionTranslationFilterTests {
             return null;
         }
 
-        SavedRequest savedRequest = (SavedRequest) session.getAttribute(SavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY);
+        DefaultSavedRequest savedRequest = (DefaultSavedRequest) session.getAttribute(DefaultSavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY);
 
-        return savedRequest.getFullRequestUrl();
+        return savedRequest.getRedirectUrl();
     }
 
     @Test
@@ -199,7 +199,7 @@ public class ExceptionTranslationFilterTests {
         doThrow(new BadCredentialsException("")).when(fc).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));
         request.setMethod("POST");
         filter.doFilter(request, new MockHttpServletResponse(), fc);
-        assertTrue(request.getSession().getAttribute(SavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY) == null);
+        assertTrue(request.getSession().getAttribute(DefaultSavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY) == null);
     }
 
     @Test(expected=IllegalArgumentException.class)

+ 8 - 8
web/src/test/java/org/springframework/security/web/authentication/AbstractProcessingFilterTests.java

@@ -49,7 +49,7 @@ import org.springframework.security.web.authentication.ExceptionMappingAuthentic
 import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
 import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
 import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
-import org.springframework.security.web.savedrequest.SavedRequest;
+import org.springframework.security.web.savedrequest.DefaultSavedRequest;
 import org.springframework.security.web.session.SessionAuthenticationStrategy;
 
 
@@ -83,7 +83,7 @@ public class AbstractProcessingFilterTests extends TestCase {
         filter.destroy();
     }
 
-    private SavedRequest makeSavedRequestForUrl() {
+    private DefaultSavedRequest makeSavedRequestForUrl() {
         MockHttpServletRequest request = createMockRequest();
         request.setMethod("GET");
         request.setServletPath("/some_protected_file.html");
@@ -91,10 +91,10 @@ public class AbstractProcessingFilterTests extends TestCase {
         request.setServerName("www.example.com");
         request.setRequestURI("/mycontext/some_protected_file.html");
 
-        return new SavedRequest(request, new PortResolverImpl());
+        return new DefaultSavedRequest(request, new PortResolverImpl());
     }
 
-//    private SavedRequest makePostSavedRequestForUrl() {
+//    private DefaultSavedRequest makePostSavedRequestForUrl() {
 //        MockHttpServletRequest request = createMockRequest();
 //        request.setServletPath("/some_protected_file.html");
 //        request.setScheme("http");
@@ -102,7 +102,7 @@ public class AbstractProcessingFilterTests extends TestCase {
 //        request.setRequestURI("/mycontext/post/some_protected_file.html");
 //        request.setMethod("POST");
 //
-//        return new SavedRequest(request, new PortResolverImpl());
+//        return new DefaultSavedRequest(request, new PortResolverImpl());
 //    }
 
     protected void setUp() throws Exception {
@@ -327,7 +327,7 @@ public class AbstractProcessingFilterTests extends TestCase {
             throws Exception {
         // Setup our HTTP request
         MockHttpServletRequest request = createMockRequest();
-        request.getSession().setAttribute(SavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY, makeSavedRequestForUrl());
+        request.getSession().setAttribute(DefaultSavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY, makeSavedRequestForUrl());
 
         // Setup our filter configuration
         MockFilterConfig config = new MockFilterConfig(null, null);
@@ -352,7 +352,7 @@ public class AbstractProcessingFilterTests extends TestCase {
     public void testSuccessfulAuthenticationCausesRedirectToSessionSpecifiedUrl() throws Exception {
         // Setup our HTTP request
         MockHttpServletRequest request = createMockRequest();
-        request.getSession().setAttribute(SavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY, makeSavedRequestForUrl());
+        request.getSession().setAttribute(DefaultSavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY, makeSavedRequestForUrl());
 
         // Setup our filter configuration
         MockFilterConfig config = new MockFilterConfig(null, null);
@@ -367,7 +367,7 @@ public class AbstractProcessingFilterTests extends TestCase {
 
         // Test
         executeFilterInContainerSimulator(config, filter, request, response, chain);
-        assertEquals(makeSavedRequestForUrl().getFullRequestUrl(), response.getRedirectedUrl());
+        assertEquals(makeSavedRequestForUrl().getRedirectUrl(), response.getRedirectedUrl());
         assertNotNull(SecurityContextHolder.getContext().getAuthentication());
     }
 

+ 33 - 0
web/src/test/java/org/springframework/security/web/savedrequest/HttpSessionRequestCacheTests.java

@@ -0,0 +1,33 @@
+package org.springframework.security.web.savedrequest;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+
+/**
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ * @since 3.0
+ */
+public class HttpSessionRequestCacheTests {
+
+    @Test
+    public void originalGetRequestDoesntMatchIncomingPost() {
+        HttpSessionRequestCache cache = new HttpSessionRequestCache();
+
+        MockHttpServletRequest request = new MockHttpServletRequest("GET", "/destination");
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        cache.saveRequest(request, response);
+        assertNotNull(request.getSession().getAttribute(DefaultSavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY));
+        assertNotNull(cache.getRequest(request, response));
+
+        MockHttpServletRequest newRequest = new MockHttpServletRequest("POST", "/destination");
+        newRequest.setSession(request.getSession());
+        assertNull(cache.getMatchingRequest(newRequest, response));
+
+    }
+
+}

+ 2 - 4
web/src/test/java/org/springframework/security/web/savedrequest/RequestCacheAwareFilterTests.java

@@ -9,7 +9,6 @@ import org.springframework.mock.web.MockHttpServletResponse;
 
 public class RequestCacheAwareFilterTests {
 
-
     @Test
     public void savedRequestIsRemovedAfterMatch() throws Exception {
         RequestCacheAwareFilter filter = new RequestCacheAwareFilter();
@@ -18,10 +17,9 @@ public class RequestCacheAwareFilterTests {
         MockHttpServletRequest request = new MockHttpServletRequest("POST", "/destination");
         MockHttpServletResponse response = new MockHttpServletResponse();
         cache.saveRequest(request, response);
-        assertNotNull(request.getSession().getAttribute(SavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY));
+        assertNotNull(request.getSession().getAttribute(DefaultSavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY));
 
         filter.doFilter(request, response, new MockFilterChain());
-        assertNull(request.getSession().getAttribute(SavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY));
+        assertNull(request.getSession().getAttribute(DefaultSavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY));
     }
-
 }

+ 2 - 2
web/src/test/java/org/springframework/security/web/savedrequest/SavedRequestAwareWrapperTests.java

@@ -13,13 +13,13 @@ import org.junit.Test;
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.security.web.PortResolverImpl;
 import org.springframework.security.web.savedrequest.FastHttpDateFormat;
-import org.springframework.security.web.savedrequest.SavedRequest;
+import org.springframework.security.web.savedrequest.DefaultSavedRequest;
 import org.springframework.security.web.savedrequest.SavedRequestAwareWrapper;
 
 public class SavedRequestAwareWrapperTests {
 
     private SavedRequestAwareWrapper createWrapper(MockHttpServletRequest requestToSave, MockHttpServletRequest requestToWrap) {
-        SavedRequest saved = requestToSave == null ? null : new SavedRequest(requestToSave, new PortResolverImpl());
+        DefaultSavedRequest saved = requestToSave == null ? null : new DefaultSavedRequest(requestToSave, new PortResolverImpl());
         return new SavedRequestAwareWrapper(saved, requestToWrap);
     }
 

+ 3 - 3
web/src/test/java/org/springframework/security/web/savedrequest/SavedRequestTests.java

@@ -4,7 +4,7 @@ import static org.junit.Assert.*;
 
 import org.junit.Test;
 import org.springframework.security.MockPortResolver;
-import org.springframework.security.web.savedrequest.SavedRequest;
+import org.springframework.security.web.savedrequest.DefaultSavedRequest;
 import org.springframework.mock.web.MockHttpServletRequest;
 
 /**
@@ -16,7 +16,7 @@ public class SavedRequestTests {
     public void headersAreCaseInsensitive() throws Exception {
         MockHttpServletRequest request = new MockHttpServletRequest();
         request.addHeader("USER-aGenT", "Mozilla");
-        SavedRequest saved = new SavedRequest(request, new MockPortResolver(8080, 8443));
+        DefaultSavedRequest saved = new DefaultSavedRequest(request, new MockPortResolver(8080, 8443));
         assertEquals("Mozilla", saved.getHeaderValues("user-agent").next());
     }
 
@@ -25,7 +25,7 @@ public class SavedRequestTests {
     public void parametersAreCaseInsensitive() throws Exception {
         MockHttpServletRequest request = new MockHttpServletRequest();
         request.addParameter("ThisIsATest", "Hi mom");
-        SavedRequest saved = new SavedRequest(request, new MockPortResolver(8080, 8443));
+        DefaultSavedRequest saved = new DefaultSavedRequest(request, new MockPortResolver(8080, 8443));
         assertEquals("Hi mom", saved.getParameterValues("thisisatest")[0]);
     }
 }

+ 3 - 3
web/src/test/java/org/springframework/security/web/session/DefaultSessionAuthenticationStrategyTests.java

@@ -10,7 +10,7 @@ import org.junit.Test;
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
 import org.springframework.security.core.Authentication;
-import org.springframework.security.web.savedrequest.SavedRequest;
+import org.springframework.security.web.savedrequest.DefaultSavedRequest;
 
 /**
  *
@@ -48,12 +48,12 @@ public class DefaultSessionAuthenticationStrategyTests {
         HttpServletRequest request = new MockHttpServletRequest();
         HttpSession session = request.getSession();
         session.setAttribute("blah", "blah");
-        session.setAttribute(SavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY, "SavedRequest");
+        session.setAttribute(DefaultSavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY, "DefaultSavedRequest");
 
         strategy.onAuthentication(mock(Authentication.class), request, new MockHttpServletResponse());
 
         assertNull(request.getSession().getAttribute("blah"));
-        assertNotNull(request.getSession().getAttribute(SavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY));
+        assertNotNull(request.getSession().getAttribute(DefaultSavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY));
     }
 
     @Test