Configuration of session management strategies
This commit adds an ExpiredSessionStrategy for the ConcurrentSessionFilter analogous to the InvalidSessionStrategy for the SessionManagementFilter. It also adds a configuration option for both the InvalidSessionStrategy and ExpiredSessionStrategy to the XML namespace and Java configuration. Fixes gh-3794 Fixes gh-3795
This commit is contained in:
+14
-22
@@ -17,7 +17,6 @@
|
||||
package org.springframework.security.web.session;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
@@ -35,7 +34,6 @@ import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.security.web.authentication.logout.CompositeLogoutHandler;
|
||||
import org.springframework.security.web.authentication.logout.LogoutHandler;
|
||||
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
||||
import org.springframework.security.web.util.UrlUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.filter.GenericFilterBean;
|
||||
|
||||
@@ -51,8 +49,8 @@ import org.springframework.web.filter.GenericFilterBean;
|
||||
* as expired. If it has been marked as expired, the configured logout handlers will be
|
||||
* called (as happens with
|
||||
* {@link org.springframework.security.web.authentication.logout.LogoutFilter}), typically
|
||||
* to invalidate the session. A redirect to the expiredURL specified will be performed,
|
||||
* and the session invalidation will cause an
|
||||
* to invalidate the session. To handle the expired session a call to the {@link ExpiredSessionStrategy} is made.
|
||||
* The session invalidation will cause an
|
||||
* {@link org.springframework.security.web.session.HttpSessionDestroyedEvent} to be
|
||||
* published via the
|
||||
* {@link org.springframework.security.web.session.HttpSessionEventPublisher} registered
|
||||
@@ -61,15 +59,15 @@ import org.springframework.web.filter.GenericFilterBean;
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Eddú Meléndez
|
||||
* @author Marten Deinum
|
||||
*/
|
||||
public class ConcurrentSessionFilter extends GenericFilterBean {
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
private SessionRegistry sessionRegistry;
|
||||
private String expiredUrl;
|
||||
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
|
||||
private final SessionRegistry sessionRegistry;
|
||||
private LogoutHandler handlers = new CompositeLogoutHandler(new SecurityContextLogoutHandler());
|
||||
private ExpiredSessionStrategy expiredSessionStrategy;
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
@@ -81,17 +79,13 @@ public class ConcurrentSessionFilter extends GenericFilterBean {
|
||||
|
||||
public ConcurrentSessionFilter(SessionRegistry sessionRegistry, String expiredUrl) {
|
||||
Assert.notNull(sessionRegistry, "SessionRegistry required");
|
||||
Assert.isTrue(expiredUrl == null || UrlUtils.isValidRedirectUrl(expiredUrl),
|
||||
expiredUrl + " isn't a valid redirect URL");
|
||||
this.sessionRegistry = sessionRegistry;
|
||||
this.expiredUrl = expiredUrl;
|
||||
this.expiredSessionStrategy = new SimpleRedirectExpiredSessionStrategy(expiredUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(sessionRegistry, "SessionRegistry required");
|
||||
Assert.isTrue(expiredUrl == null || UrlUtils.isValidRedirectUrl(expiredUrl),
|
||||
expiredUrl + " isn't a valid redirect URL");
|
||||
}
|
||||
|
||||
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
|
||||
@@ -108,12 +102,14 @@ public class ConcurrentSessionFilter extends GenericFilterBean {
|
||||
if (info != null) {
|
||||
if (info.isExpired()) {
|
||||
// Expired - abort processing
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Requested session ID "
|
||||
+ request.getRequestedSessionId() + " has expired.");
|
||||
}
|
||||
doLogout(request, response);
|
||||
|
||||
String targetUrl = determineExpiredUrl(request, info);
|
||||
|
||||
if (targetUrl != null) {
|
||||
redirectStrategy.sendRedirect(request, response, targetUrl);
|
||||
if (this.expiredSessionStrategy != null) {
|
||||
this.expiredSessionStrategy.onExpiredSessionDetected(request, response);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -136,10 +132,6 @@ public class ConcurrentSessionFilter extends GenericFilterBean {
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
protected String determineExpiredUrl(HttpServletRequest request,
|
||||
SessionInformation info) {
|
||||
return expiredUrl;
|
||||
}
|
||||
|
||||
private void doLogout(HttpServletRequest request, HttpServletResponse response) {
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
@@ -151,7 +143,7 @@ public class ConcurrentSessionFilter extends GenericFilterBean {
|
||||
this.handlers = new CompositeLogoutHandler(handlers);
|
||||
}
|
||||
|
||||
public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
|
||||
this.redirectStrategy = redirectStrategy;
|
||||
public void setExpiredSessionStrategy(ExpiredSessionStrategy expiredSessionStrategy) {
|
||||
this.expiredSessionStrategy=expiredSessionStrategy;
|
||||
}
|
||||
}
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
*
|
||||
* 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.session;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Determines the behaviour of the {@code ConcurrentSessionFilter} when an expired session
|
||||
* is detected in the {@code ConcurrentSessionFilter}.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public interface ExpiredSessionStrategy {
|
||||
|
||||
void onExpiredSessionDetected(HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException, ServletException;
|
||||
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* 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.session;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Performs a redirect to a fixed URL when an expired session is detected by the
|
||||
* {@code ConcurrentSessionFilter}.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public final class SimpleRedirectExpiredSessionStrategy implements ExpiredSessionStrategy {
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
private final String destinationUrl;
|
||||
private final RedirectStrategy redirectStrategy;
|
||||
|
||||
public SimpleRedirectExpiredSessionStrategy(String invalidSessionUrl) {
|
||||
this(invalidSessionUrl, new DefaultRedirectStrategy());
|
||||
}
|
||||
|
||||
public SimpleRedirectExpiredSessionStrategy(String invalidSessionUrl, RedirectStrategy redirectStrategy) {
|
||||
Assert.isTrue(UrlUtils.isValidRedirectUrl(invalidSessionUrl),
|
||||
"url must start with '/' or with 'http(s)'");
|
||||
this.destinationUrl=invalidSessionUrl;
|
||||
this.redirectStrategy=redirectStrategy;
|
||||
}
|
||||
|
||||
public void onExpiredSessionDetected(HttpServletRequest request,
|
||||
HttpServletResponse response) throws IOException {
|
||||
logger.debug("Redirecting to '" + destinationUrl + "'");
|
||||
redirectStrategy.sendRedirect(request, response, destinationUrl);
|
||||
}
|
||||
|
||||
}
|
||||
+12
-17
@@ -16,11 +16,7 @@
|
||||
|
||||
package org.springframework.security.web.concurrent;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
|
||||
import org.junit.Test;
|
||||
@@ -29,10 +25,13 @@ import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
import org.springframework.security.core.session.SessionRegistry;
|
||||
import org.springframework.security.core.session.SessionRegistryImpl;
|
||||
import org.springframework.security.web.DefaultRedirectStrategy;
|
||||
import org.springframework.security.web.authentication.logout.LogoutHandler;
|
||||
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
||||
import org.springframework.security.web.session.ConcurrentSessionFilter;
|
||||
import org.springframework.security.web.session.SimpleRedirectExpiredSessionStrategy;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Tests {@link ConcurrentSessionFilter}.
|
||||
@@ -56,9 +55,10 @@ public class ConcurrentSessionFilterTests {
|
||||
registry.getSessionInformation(session.getId()).expireNow();
|
||||
|
||||
// Setup our test fixture and registry to want this session to be expired
|
||||
ConcurrentSessionFilter filter = new ConcurrentSessionFilter(registry,
|
||||
"/expired.jsp");
|
||||
filter.setRedirectStrategy(new DefaultRedirectStrategy());
|
||||
|
||||
SimpleRedirectExpiredSessionStrategy expiredSessionStrategy = new SimpleRedirectExpiredSessionStrategy("/expired.jsp");
|
||||
ConcurrentSessionFilter filter = new ConcurrentSessionFilter(registry);
|
||||
filter.setExpiredSessionStrategy(expiredSessionStrategy);
|
||||
filter.setLogoutHandlers(new LogoutHandler[] { new SecurityContextLogoutHandler() });
|
||||
filter.afterPropertiesSet();
|
||||
|
||||
@@ -97,11 +97,6 @@ public class ConcurrentSessionFilterTests {
|
||||
new ConcurrentSessionFilter(null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void detectsInvalidUrl() throws Exception {
|
||||
new ConcurrentSessionFilter(new SessionRegistryImpl(), "ImNotValid");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lastRequestTimeUpdatesCorrectly() throws Exception {
|
||||
// Setup our HTTP request
|
||||
@@ -115,11 +110,11 @@ public class ConcurrentSessionFilterTests {
|
||||
// Setup our test fixture
|
||||
SessionRegistry registry = new SessionRegistryImpl();
|
||||
registry.registerNewSession(session.getId(), "principal");
|
||||
ConcurrentSessionFilter filter = new ConcurrentSessionFilter(registry,
|
||||
"/expired.jsp");
|
||||
SimpleRedirectExpiredSessionStrategy expiredSessionStrategy = new SimpleRedirectExpiredSessionStrategy("/expired.jsp");
|
||||
ConcurrentSessionFilter filter = new ConcurrentSessionFilter(registry);
|
||||
filter.setExpiredSessionStrategy(expiredSessionStrategy);
|
||||
|
||||
Date lastRequest = registry.getSessionInformation(session.getId())
|
||||
.getLastRequest();
|
||||
Date lastRequest = registry.getSessionInformation(session.getId()).getLastRequest();
|
||||
|
||||
Thread.sleep(1000);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user