SEC-1754: Added an InvalidSessionStrategy to allow SessionManagementFilter to delegate out the behaviour when an invalid session identifier is submitted.
This commit is contained in:
parent
ac3d8b25f2
commit
a1c714cff4
|
@ -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);
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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><concurrency-control></literal> Element</title>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue