فهرست منبع

SEC-1754: Added an InvalidSessionStrategy to allow SessionManagementFilter to delegate out the behaviour when an invalid session identifier is submitted.

Luke Taylor 14 سال پیش
والد
کامیت
a1c714cff4

+ 2 - 1
config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java

@@ -46,6 +46,7 @@ import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
 import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
 import org.springframework.security.web.session.ConcurrentSessionFilter;
 import org.springframework.security.web.session.SessionManagementFilter;
+import org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy;
 import org.springframework.util.StringUtils;
 import org.springframework.util.xml.DomUtils;
 import org.w3c.dom.Element;
@@ -270,7 +271,7 @@ class HttpConfigurationBuilder {
         }
 
         if (StringUtils.hasText(invalidSessionUrl)) {
-            sessionMgmtFilter.addPropertyValue("invalidSessionUrl", invalidSessionUrl);
+            sessionMgmtFilter.addPropertyValue("invalidSessionStrategy", new SimpleRedirectInvalidSessionStrategy(invalidSessionUrl));
         }
 
         sessionMgmtFilter.addPropertyReference("sessionAuthenticationStrategy", sessionAuthStratRef);

+ 2 - 2
config/src/test/groovy/org/springframework/security/config/http/SessionManagementConfigTests.groovy

@@ -124,7 +124,7 @@ class SessionManagementConfigTests extends AbstractHttpConfigTests {
         Object sessionRegistryFromFormLoginFilter = FieldUtils.getFieldValue(
                 getFilter(UsernamePasswordAuthenticationFilter.class),"sessionStrategy.sessionRegistry");
         Object sessionRegistryFromMgmtFilter = FieldUtils.getFieldValue(
-                getFilter(SessionManagementFilter.class),"sessionStrategy.sessionRegistry");
+                getFilter(SessionManagementFilter.class),"sessionAuthenticationStrategy.sessionRegistry");
 
         assertSame(sessionRegistry, sessionRegistryFromConcurrencyFilter);
         assertSame(sessionRegistry, sessionRegistryFromMgmtFilter);
@@ -183,7 +183,7 @@ class SessionManagementConfigTests extends AbstractHttpConfigTests {
 
         expect:
         filter instanceof SessionManagementFilter
-        filter.invalidSessionUrl == '/timeoutUrl'
+        filter.invalidSessionStrategy.destinationUrl == '/timeoutUrl'
     }
 
 }

+ 8 - 0
docs/manual/src/docbook/appendix-namespace.xml

@@ -404,6 +404,14 @@
                     configured <classname>DefaultSessionAuthenticationStrategy</classname>. See the
                     Javadoc for this class for more details. </para>
             </section>
+            <section xml:id="nsa-invalid-session-url">
+                <title><literal>invalid-session-url</literal></title>
+                <para>Setting this attribute will inject the <classname>SessionManagementFilter</classname>
+                    with a <classname>SimpleRedirectInvalidSessionStrategy</classname> configured with
+                    the attribute value. When an invalid session ID is submitted, the strategy will be invoked,
+                    redirecting to the configured URL.
+                </para>
+            </section>
         </section>
         <section xml:id="nsa-concurrent-session-control">
             <title>The <literal>&lt;concurrency-control&gt;</literal> Element</title>

+ 6 - 4
docs/manual/src/docbook/session-mgmt.xml

@@ -28,10 +28,12 @@
             invoke the configured
             <interfacename>SessionAuthenticationStrategy</interfacename>.</para>
         <para>If the user is not currently authenticated, the filter will check whether an invalid
-            session ID has been requested (because of a timeout, for example) and will redirect to
-            the configured <literal>invalidSessionUrl</literal> if set. The easiest way to configure
-            this is through the namespace, <link xlink:href="#ns-session-mgmt">as described
-            earlier</link>.</para>
+            session ID has been requested (because of a timeout, for example) and will invoke the configured
+            <interfacename>InvalidSessionStrategy</interfacename>, if one is set. The most common behaviour
+            is just to redirect to a fixed URL and this is encapsulated in the standard implementation
+            <classname>SimpleRedirectInvalidSessionStrategy</classname>. The latter is also used
+            when configuring an invalid session URL through the namespace,
+            <link xlink:href="#ns-session-mgmt">as described earlier</link>.</para>
     </section>
     <section>
         <title><interfacename>SessionAuthenticationStrategy</interfacename></title>

+ 18 - 0
web/src/main/java/org/springframework/security/web/session/InvalidSessionStrategy.java

@@ -0,0 +1,18 @@
+package org.springframework.security.web.session;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * Determines the behaviour of the {@code SessionManagementFilter} when an invalid session Id is submitted and
+ * detected in the {@code SessionManagementFilter}.
+ *
+ * @author Luke Taylor
+ */
+public interface InvalidSessionStrategy {
+
+    void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException;
+
+}

+ 14 - 24
web/src/main/java/org/springframework/security/web/session/SessionManagementFilter.java

@@ -13,8 +13,6 @@ import org.springframework.security.authentication.AuthenticationTrustResolver;
 import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.web.DefaultRedirectStrategy;
-import org.springframework.security.web.RedirectStrategy;
 import org.springframework.security.web.authentication.AuthenticationFailureHandler;
 import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
 import org.springframework.security.web.authentication.session.SessionAuthenticationException;
@@ -41,11 +39,10 @@ public class SessionManagementFilter extends GenericFilterBean {
     //~ Instance fields ================================================================================================
 
     private final SecurityContextRepository securityContextRepository;
-    private SessionAuthenticationStrategy sessionStrategy;
+    private SessionAuthenticationStrategy sessionAuthenticationStrategy;
     private final AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
-    private String invalidSessionUrl;
+    private InvalidSessionStrategy invalidSessionStrategy = null;
     private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
-    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
 
     public SessionManagementFilter(SecurityContextRepository securityContextRepository) {
         this(securityContextRepository, new SessionFixationProtectionStrategy());
@@ -55,7 +52,7 @@ public class SessionManagementFilter extends GenericFilterBean {
         Assert.notNull(securityContextRepository, "SecurityContextRepository cannot be null");
         Assert.notNull(sessionStrategy, "SessionAuthenticationStrategy cannot be null");
         this.securityContextRepository = securityContextRepository;
-        this.sessionStrategy = sessionStrategy;
+        this.sessionAuthenticationStrategy = sessionStrategy;
     }
 
     public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
@@ -76,7 +73,7 @@ public class SessionManagementFilter extends GenericFilterBean {
             if (authentication != null && !authenticationTrustResolver.isAnonymous(authentication)) {
              // The user has been authenticated during the current request, so call the session strategy
                 try {
-                    sessionStrategy.onAuthentication(authentication, request, response);
+                    sessionAuthenticationStrategy.onAuthentication(authentication, request, response);
                 } catch (SessionAuthenticationException e) {
                     // The session strategy can reject the authentication
                     logger.debug("SessionAuthenticationStrategy rejected the authentication object", e);
@@ -93,11 +90,8 @@ public class SessionManagementFilter extends GenericFilterBean {
                 if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {
                     logger.debug("Requested session ID" + request.getRequestedSessionId() + " is invalid.");
 
-                    if (invalidSessionUrl != null) {
-                        logger.debug("Starting new session (if required) and redirecting to '" + invalidSessionUrl + "'");
-                        request.getSession();
-                        redirectStrategy.sendRedirect(request, response, invalidSessionUrl);
-
+                    if (invalidSessionStrategy != null) {
+                        invalidSessionStrategy.onInvalidSessionDetected(request, response);
                         return;
                     }
                 }
@@ -115,19 +109,19 @@ public class SessionManagementFilter extends GenericFilterBean {
      * @deprecated Use constructor injection
      */
     @Deprecated
-    public void setSessionAuthenticationStrategy(SessionAuthenticationStrategy sessionStrategy) {
-        Assert.notNull(sessionStrategy, "authenticatedSessionStratedy must not be null");
-        this.sessionStrategy = sessionStrategy;
+    public void setSessionAuthenticationStrategy(SessionAuthenticationStrategy sessionAuthenticationStrategy) {
+        Assert.notNull(sessionAuthenticationStrategy, "authenticatedSessionStratedy must not be null");
+        this.sessionAuthenticationStrategy = sessionAuthenticationStrategy;
     }
 
     /**
-     * Sets the URL to which the response should be redirected if the user agent requests an invalid session Id.
-     * If the property is not set, no action will be taken.
+     * Sets the strategy which will be invoked instead of allowing the filter chain to prceed, if the user agent
+     * requests an invalid session Id. If the property is not set, no action will be taken.
      *
-     * @param invalidSessionUrl
+     * @param invalidSessionStrategy the strategy to invoke. Typically a {@link SimpleRedirectInvalidSessionStrategy}.
      */
-    public void setInvalidSessionUrl(String invalidSessionUrl) {
-        this.invalidSessionUrl = invalidSessionUrl;
+    public void setInvalidSessionStrategy(InvalidSessionStrategy invalidSessionStrategy) {
+        this.invalidSessionStrategy = invalidSessionStrategy;
     }
 
     /**
@@ -140,8 +134,4 @@ public class SessionManagementFilter extends GenericFilterBean {
         Assert.notNull(failureHandler, "failureHandler cannot be null");
         this.failureHandler = failureHandler;
     }
-
-    public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
-        this.redirectStrategy  = redirectStrategy;
-    }
 }

+ 48 - 0
web/src/main/java/org/springframework/security/web/session/SimpleRedirectInvalidSessionStrategy.java

@@ -0,0 +1,48 @@
+package org.springframework.security.web.session;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.security.web.DefaultRedirectStrategy;
+import org.springframework.security.web.RedirectStrategy;
+import org.springframework.security.web.util.UrlUtils;
+import org.springframework.util.Assert;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * Performs a redirect to a fixed URL when an invalid requested session is detected by the {@code SessionManagementFilter}.
+ *
+ * @author Luke Taylor
+ */
+public final class SimpleRedirectInvalidSessionStrategy implements InvalidSessionStrategy {
+    private final Log logger = LogFactory.getLog(getClass());
+    private final String destinationUrl;
+    private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
+    private boolean createNewSession = true;
+
+    public SimpleRedirectInvalidSessionStrategy(String invalidSessionUrl) {
+        Assert.isTrue(UrlUtils.isValidRedirectUrl(invalidSessionUrl), "url must start with '/' or with 'http(s)'");
+        this.destinationUrl = invalidSessionUrl;
+    }
+
+    public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException {
+        logger.debug("Starting new session (if required) and redirecting to '" + destinationUrl + "'");
+        if (createNewSession) {
+            request.getSession();
+        }
+        redirectStrategy.sendRedirect(request, response, destinationUrl);
+    }
+
+    /**
+     * Determines whether a new session should be created before redirecting (to avoid possible looping issues where
+     * the same session ID is sent with the redirected request). Alternatively, ensure that the configured URL
+     * does not pass through the {@code SessionManagementFilter}.
+     *
+     * @param createNewSession defaults to {@code true}.
+     */
+    public void setCreateNewSession(boolean createNewSession) {
+        this.createNewSession = createNewSession;
+    }
+}

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

@@ -121,7 +121,6 @@ public class SessionManagementFilterTests {
         SessionAuthenticationStrategy strategy = mock(SessionAuthenticationStrategy.class);
         SessionManagementFilter filter = new SessionManagementFilter(repo);
         filter.setSessionAuthenticationStrategy(strategy);
-        filter.setRedirectStrategy(new DefaultRedirectStrategy());
         MockHttpServletRequest request = new MockHttpServletRequest();
         request.setRequestedSessionId("xxx");
         request.setRequestedSessionIdValid(false);
@@ -134,7 +133,9 @@ public class SessionManagementFilterTests {
         request = new MockHttpServletRequest();
         request.setRequestedSessionId("xxx");
         request.setRequestedSessionIdValid(false);
-        filter.setInvalidSessionUrl("/timedOut");
+        SimpleRedirectInvalidSessionStrategy iss = new SimpleRedirectInvalidSessionStrategy("/timedOut");
+        iss.setCreateNewSession(true);
+        filter.setInvalidSessionStrategy(iss);
         FilterChain fc = mock(FilterChain.class);
         filter.doFilter(request, response, fc);
         verifyZeroInteractions(fc);