SEC-1229: Redesign Concurrent Session Control implementation. Renamed session strategy interface and introduced SessionAuthenticationException for rejection of session/Authentication combination.
This commit is contained in:
parent
0d7b990e0a
commit
dbcb13ad14
|
@ -33,7 +33,6 @@ import org.springframework.security.access.vote.RoleVoter;
|
||||||
import org.springframework.security.authentication.AnonymousAuthenticationProvider;
|
import org.springframework.security.authentication.AnonymousAuthenticationProvider;
|
||||||
import org.springframework.security.authentication.ProviderManager;
|
import org.springframework.security.authentication.ProviderManager;
|
||||||
import org.springframework.security.authentication.RememberMeAuthenticationProvider;
|
import org.springframework.security.authentication.RememberMeAuthenticationProvider;
|
||||||
import org.springframework.security.authentication.concurrent.ConcurrentSessionControllerImpl;
|
|
||||||
import org.springframework.security.config.BeanIds;
|
import org.springframework.security.config.BeanIds;
|
||||||
import org.springframework.security.config.Elements;
|
import org.springframework.security.config.Elements;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
|
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
|
||||||
|
@ -64,7 +63,7 @@ import org.springframework.security.web.context.SecurityContextPersistenceFilter
|
||||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
|
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
|
||||||
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
|
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
|
||||||
import org.springframework.security.web.session.ConcurrentSessionControlAuthenticatedSessionStrategy;
|
import org.springframework.security.web.session.ConcurrentSessionControlAuthenticatedSessionStrategy;
|
||||||
import org.springframework.security.web.session.DefaultAuthenticatedSessionStrategy;
|
import org.springframework.security.web.session.DefaultSessionAuthenticationStrategy;
|
||||||
import org.springframework.security.web.session.SessionManagementFilter;
|
import org.springframework.security.web.session.SessionManagementFilter;
|
||||||
import org.springframework.security.web.util.AntUrlPathMatcher;
|
import org.springframework.security.web.util.AntUrlPathMatcher;
|
||||||
import org.springframework.security.web.util.RegexUrlPathMatcher;
|
import org.springframework.security.web.util.RegexUrlPathMatcher;
|
||||||
|
@ -130,8 +129,6 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
|
|
||||||
private static final String ATT_DISABLE_URL_REWRITING = "disable-url-rewriting";
|
private static final String ATT_DISABLE_URL_REWRITING = "disable-url-rewriting";
|
||||||
|
|
||||||
private static final String ATT_SESSION_CONTROLLER_REF = "session-controller-ref";
|
|
||||||
|
|
||||||
static final String OPEN_ID_AUTHENTICATION_PROCESSING_FILTER_CLASS = "org.springframework.security.openid.OpenIDAuthenticationProcessingFilter";
|
static final String OPEN_ID_AUTHENTICATION_PROCESSING_FILTER_CLASS = "org.springframework.security.openid.OpenIDAuthenticationProcessingFilter";
|
||||||
static final String OPEN_ID_AUTHENTICATION_PROVIDER_CLASS = "org.springframework.security.openid.OpenIDAuthenticationProvider";
|
static final String OPEN_ID_AUTHENTICATION_PROVIDER_CLASS = "org.springframework.security.openid.OpenIDAuthenticationProvider";
|
||||||
static final String OPEN_ID_CONSUMER_CLASS = "org.springframework.security.openid.OpenID4JavaConsumer";
|
static final String OPEN_ID_CONSUMER_CLASS = "org.springframework.security.openid.OpenID4JavaConsumer";
|
||||||
|
@ -726,45 +723,6 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
return sessionControlFilter;
|
return sessionControlFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BeanReference createConcurrentSessionController(Element elt, BeanDefinition filter, BeanReference sessionRegistry, ParserContext pc) {
|
|
||||||
Element sessionCtrlElement = DomUtils.getChildElementByTagName(elt, Elements.CONCURRENT_SESSIONS);
|
|
||||||
|
|
||||||
// Check for a custom controller
|
|
||||||
// String sessionControllerRef = sessionCtrlElement.getAttribute(ATT_SESSION_CONTROLLER_REF);
|
|
||||||
//
|
|
||||||
// if (StringUtils.hasText(sessionControllerRef)) {
|
|
||||||
// if (!StringUtils.hasText(sessionCtrlElement.getAttribute(ConcurrentSessionsBeanDefinitionParser.ATT_SESSION_REGISTRY_REF))) {
|
|
||||||
// pc.getReaderContext().error("Use of " + ATT_SESSION_CONTROLLER_REF + " requires that " +
|
|
||||||
// ConcurrentSessionsBeanDefinitionParser.ATT_SESSION_REGISTRY_REF + " is also set.",
|
|
||||||
// pc.extractSource(sessionCtrlElement));
|
|
||||||
// }
|
|
||||||
// return new RuntimeBeanReference(sessionControllerRef);
|
|
||||||
// }
|
|
||||||
|
|
||||||
BeanDefinitionBuilder controllerBuilder = BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionControllerImpl.class);
|
|
||||||
controllerBuilder.getRawBeanDefinition().setSource(filter.getSource());
|
|
||||||
controllerBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
|
||||||
controllerBuilder.addPropertyValue("sessionRegistry", sessionRegistry);
|
|
||||||
|
|
||||||
String maxSessions = sessionCtrlElement.getAttribute("max-sessions");
|
|
||||||
|
|
||||||
if (StringUtils.hasText(maxSessions)) {
|
|
||||||
controllerBuilder.addPropertyValue("maximumSessions", maxSessions);
|
|
||||||
}
|
|
||||||
|
|
||||||
String exceptionIfMaximumExceeded = sessionCtrlElement.getAttribute("exception-if-maximum-exceeded");
|
|
||||||
|
|
||||||
if (StringUtils.hasText(exceptionIfMaximumExceeded)) {
|
|
||||||
controllerBuilder.addPropertyValue("exceptionIfMaximumExceeded", exceptionIfMaximumExceeded);
|
|
||||||
}
|
|
||||||
|
|
||||||
BeanDefinition controller = controllerBuilder.getBeanDefinition();
|
|
||||||
|
|
||||||
String id = pc.getReaderContext().registerWithGeneratedName(controller);
|
|
||||||
pc.registerComponent(new BeanComponentDefinition(controller, id));
|
|
||||||
return new RuntimeBeanReference(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private BeanReference createRequestCache(Element element, ParserContext pc, boolean allowSessionCreation,
|
private BeanReference createRequestCache(Element element, ParserContext pc, boolean allowSessionCreation,
|
||||||
String portMapperName) {
|
String portMapperName) {
|
||||||
BeanDefinitionBuilder requestCache = BeanDefinitionBuilder.rootBeanDefinition(HttpSessionRequestCache.class);
|
BeanDefinitionBuilder requestCache = BeanDefinitionBuilder.rootBeanDefinition(HttpSessionRequestCache.class);
|
||||||
|
@ -948,7 +906,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
sessionStrategy.addPropertyValue("exceptionIfMaximumExceeded", exceptionIfMaximumExceeded);
|
sessionStrategy.addPropertyValue("exceptionIfMaximumExceeded", exceptionIfMaximumExceeded);
|
||||||
}
|
}
|
||||||
} else if (sessionFixationProtectionRequired || StringUtils.hasText(invalidSessionUrl)) {
|
} else if (sessionFixationProtectionRequired || StringUtils.hasText(invalidSessionUrl)) {
|
||||||
sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(DefaultAuthenticatedSessionStrategy.class);
|
sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(DefaultSessionAuthenticationStrategy.class);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ import org.springframework.security.web.authentication.www.BasicProcessingFilter
|
||||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
|
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
|
||||||
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
|
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
|
||||||
import org.springframework.security.web.session.AuthenticatedSessionStrategy;
|
import org.springframework.security.web.session.SessionAuthenticationStrategy;
|
||||||
import org.springframework.security.web.session.SessionManagementFilter;
|
import org.springframework.security.web.session.SessionManagementFilter;
|
||||||
import org.springframework.security.web.wrapper.SecurityContextHolderAwareRequestFilter;
|
import org.springframework.security.web.wrapper.SecurityContextHolderAwareRequestFilter;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
@ -115,7 +115,7 @@ public class HttpSecurityBeanDefinitionParserTests {
|
||||||
|
|
||||||
checkAutoConfigFilters(filterList);
|
checkAutoConfigFilters(filterList);
|
||||||
|
|
||||||
assertEquals(true, FieldUtils.getFieldValue(appContext.getBean("_filterChainProxy"), "stripQueryStringFromUrls"));
|
assertEquals(true, FieldUtils.getFieldValue(appContext.getBean(BeanIds.FILTER_CHAIN_PROXY), "stripQueryStringFromUrls"));
|
||||||
assertEquals(true, FieldUtils.getFieldValue(filterList.get(AUTO_CONFIG_FILTERS-1), "securityMetadataSource.stripQueryStringFromUrls"));
|
assertEquals(true, FieldUtils.getFieldValue(filterList.get(AUTO_CONFIG_FILTERS-1), "securityMetadataSource.stripQueryStringFromUrls"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,8 +138,8 @@ public class HttpSecurityBeanDefinitionParserTests {
|
||||||
assertTrue(filters.next() instanceof RequestCacheAwareFilter);
|
assertTrue(filters.next() instanceof RequestCacheAwareFilter);
|
||||||
assertTrue(filters.next() instanceof SecurityContextHolderAwareRequestFilter);
|
assertTrue(filters.next() instanceof SecurityContextHolderAwareRequestFilter);
|
||||||
assertTrue(filters.next() instanceof AnonymousProcessingFilter);
|
assertTrue(filters.next() instanceof AnonymousProcessingFilter);
|
||||||
assertTrue(filters.next() instanceof ExceptionTranslationFilter);
|
|
||||||
assertTrue(filters.next() instanceof SessionManagementFilter);
|
assertTrue(filters.next() instanceof SessionManagementFilter);
|
||||||
|
assertTrue(filters.next() instanceof ExceptionTranslationFilter);
|
||||||
Object fsiObj = filters.next();
|
Object fsiObj = filters.next();
|
||||||
assertTrue(fsiObj instanceof FilterSecurityInterceptor);
|
assertTrue(fsiObj instanceof FilterSecurityInterceptor);
|
||||||
FilterSecurityInterceptor fsi = (FilterSecurityInterceptor) fsiObj;
|
FilterSecurityInterceptor fsi = (FilterSecurityInterceptor) fsiObj;
|
||||||
|
@ -363,7 +363,7 @@ public class HttpSecurityBeanDefinitionParserTests {
|
||||||
setContext("<http access-denied-page='/access-denied'><http-basic /></http>" + AUTH_PROVIDER_XML);
|
setContext("<http access-denied-page='/access-denied'><http-basic /></http>" + AUTH_PROVIDER_XML);
|
||||||
List<Filter> filters = getFilters("/someurl");
|
List<Filter> filters = getFilters("/someurl");
|
||||||
|
|
||||||
ExceptionTranslationFilter etf = (ExceptionTranslationFilter) filters.get(filters.size() - 3);
|
ExceptionTranslationFilter etf = (ExceptionTranslationFilter) filters.get(filters.size() - 2);
|
||||||
|
|
||||||
assertEquals("/access-denied", FieldUtils.getFieldValue(etf, "accessDeniedHandler.errorPage"));
|
assertEquals("/access-denied", FieldUtils.getFieldValue(etf, "accessDeniedHandler.errorPage"));
|
||||||
}
|
}
|
||||||
|
@ -755,7 +755,7 @@ public class HttpSecurityBeanDefinitionParserTests {
|
||||||
"<http auto-config='true'>" +
|
"<http auto-config='true'>" +
|
||||||
" <concurrent-session-control max-sessions='2' exception-if-maximum-exceeded='true' />" +
|
" <concurrent-session-control max-sessions='2' exception-if-maximum-exceeded='true' />" +
|
||||||
"</http>" + AUTH_PROVIDER_XML);
|
"</http>" + AUTH_PROVIDER_XML);
|
||||||
AuthenticatedSessionStrategy seshStrategy = (AuthenticatedSessionStrategy) FieldUtils.getFieldValue(
|
SessionAuthenticationStrategy seshStrategy = (SessionAuthenticationStrategy) FieldUtils.getFieldValue(
|
||||||
getFilter(SessionManagementFilter.class), "sessionStrategy");
|
getFilter(SessionManagementFilter.class), "sessionStrategy");
|
||||||
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("bob", "pass");
|
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("bob", "pass");
|
||||||
// Register 2 sessions and then check a third
|
// Register 2 sessions and then check a third
|
||||||
|
@ -782,7 +782,7 @@ public class HttpSecurityBeanDefinitionParserTests {
|
||||||
"<http auto-config='true' entry-point-ref='entryPoint'/>" +
|
"<http auto-config='true' entry-point-ref='entryPoint'/>" +
|
||||||
"<b:bean id='entryPoint' class='" + MockEntryPoint.class.getName() + "'>" +
|
"<b:bean id='entryPoint' class='" + MockEntryPoint.class.getName() + "'>" +
|
||||||
"</b:bean>" + AUTH_PROVIDER_XML);
|
"</b:bean>" + AUTH_PROVIDER_XML);
|
||||||
ExceptionTranslationFilter etf = (ExceptionTranslationFilter) getFilters("/someurl").get(AUTO_CONFIG_FILTERS-3);
|
ExceptionTranslationFilter etf = (ExceptionTranslationFilter) getFilters("/someurl").get(AUTO_CONFIG_FILTERS-2);
|
||||||
assertTrue("ExceptionTranslationFilter should be configured with custom entry point",
|
assertTrue("ExceptionTranslationFilter should be configured with custom entry point",
|
||||||
etf.getAuthenticationEntryPoint() instanceof MockEntryPoint);
|
etf.getAuthenticationEntryPoint() instanceof MockEntryPoint);
|
||||||
}
|
}
|
||||||
|
@ -810,8 +810,7 @@ public class HttpSecurityBeanDefinitionParserTests {
|
||||||
setContext(
|
setContext(
|
||||||
"<http auto-config='true' session-fixation-protection='none'/>" + AUTH_PROVIDER_XML);
|
"<http auto-config='true' session-fixation-protection='none'/>" + AUTH_PROVIDER_XML);
|
||||||
List<Filter> filters = getFilters("/someurl");
|
List<Filter> filters = getFilters("/someurl");
|
||||||
assertTrue(filters.get(8) instanceof ExceptionTranslationFilter);
|
assertFalse(filters.get(8) instanceof SessionManagementFilter);
|
||||||
assertFalse(filters.get(9) instanceof SessionManagementFilter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -820,7 +819,7 @@ public class HttpSecurityBeanDefinitionParserTests {
|
||||||
"<http auto-config='true' session-fixation-protection='none'" +
|
"<http auto-config='true' session-fixation-protection='none'" +
|
||||||
" invalid-session-url='/timeoutUrl' />" + AUTH_PROVIDER_XML);
|
" invalid-session-url='/timeoutUrl' />" + AUTH_PROVIDER_XML);
|
||||||
List<Filter> filters = getFilters("/someurl");
|
List<Filter> filters = getFilters("/someurl");
|
||||||
Object filter = filters.get(9);
|
Object filter = filters.get(8);
|
||||||
assertTrue(filter instanceof SessionManagementFilter);
|
assertTrue(filter instanceof SessionManagementFilter);
|
||||||
assertEquals("/timeoutUrl", FieldUtils.getProtectedFieldValue("invalidSessionUrl", filter));
|
assertEquals("/timeoutUrl", FieldUtils.getProtectedFieldValue("invalidSessionUrl", filter));
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.web.session.AuthenticatedSessionStrategy;
|
import org.springframework.security.web.session.SessionAuthenticationStrategy;
|
||||||
import org.springframework.security.web.session.NullAuthenticatedSessionStrategy;
|
import org.springframework.security.web.session.NullAuthenticatedSessionStrategy;
|
||||||
import org.springframework.security.web.util.UrlUtils;
|
import org.springframework.security.web.util.UrlUtils;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
@ -130,7 +130,7 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt
|
||||||
|
|
||||||
private boolean continueChainBeforeSuccessfulAuthentication = false;
|
private boolean continueChainBeforeSuccessfulAuthentication = false;
|
||||||
|
|
||||||
private AuthenticatedSessionStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();
|
private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();
|
||||||
|
|
||||||
private boolean allowSessionCreation = true;
|
private boolean allowSessionCreation = true;
|
||||||
|
|
||||||
|
@ -273,7 +273,7 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt
|
||||||
* Default behaviour for successful authentication.
|
* Default behaviour for successful authentication.
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>Sets the successful <tt>Authentication</tt> object on the {@link SecurityContextHolder}</li>
|
* <li>Sets the successful <tt>Authentication</tt> object on the {@link SecurityContextHolder}</li>
|
||||||
* <li>Invokes the configured {@link AuthenticatedSessionStrategy} to handle any session-related behaviour
|
* <li>Invokes the configured {@link SessionAuthenticationStrategy} to handle any session-related behaviour
|
||||||
* (such as creating a new session to protect against session-fixation attacks).</li>
|
* (such as creating a new session to protect against session-fixation attacks).</li>
|
||||||
* <li>Informs the configured <tt>RememberMeServices</tt> of the successful login</li>
|
* <li>Informs the configured <tt>RememberMeServices</tt> of the successful login</li>
|
||||||
* <li>Fires an {@link InteractiveAuthenticationSuccessEvent} via the configured
|
* <li>Fires an {@link InteractiveAuthenticationSuccessEvent} via the configured
|
||||||
|
@ -400,7 +400,7 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt
|
||||||
* @param sessionStrategy the implementation to use. If not set a null implementation is
|
* @param sessionStrategy the implementation to use. If not set a null implementation is
|
||||||
* used.
|
* used.
|
||||||
*/
|
*/
|
||||||
public void setAuthenticatedSessionStrategy(AuthenticatedSessionStrategy sessionStrategy) {
|
public void setAuthenticatedSessionStrategy(SessionAuthenticationStrategy sessionStrategy) {
|
||||||
this.sessionStrategy = sessionStrategy;
|
this.sessionStrategy = sessionStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ import org.springframework.util.Assert;
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class ConcurrentSessionControlAuthenticatedSessionStrategy extends DefaultAuthenticatedSessionStrategy
|
public class ConcurrentSessionControlAuthenticatedSessionStrategy extends DefaultSessionAuthenticationStrategy
|
||||||
implements MessageSourceAware {
|
implements MessageSourceAware {
|
||||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||||
private final SessionRegistry sessionRegistry;
|
private final SessionRegistry sessionRegistry;
|
||||||
|
|
|
@ -16,7 +16,7 @@ import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.web.savedrequest.SavedRequest;
|
import org.springframework.security.web.savedrequest.SavedRequest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default implementation of {@link AuthenticatedSessionStrategy}.
|
* The default implementation of {@link SessionAuthenticationStrategy}.
|
||||||
* <p>
|
* <p>
|
||||||
* Creates a new session for the newly authenticated user if they already have a session (as a defence against
|
* Creates a new session for the newly authenticated user if they already have a session (as a defence against
|
||||||
* session-fixation protection attacks), and copies their
|
* session-fixation protection attacks), and copies their
|
||||||
|
@ -32,7 +32,7 @@ import org.springframework.security.web.savedrequest.SavedRequest;
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class DefaultAuthenticatedSessionStrategy implements AuthenticatedSessionStrategy {
|
public class DefaultSessionAuthenticationStrategy implements SessionAuthenticationStrategy {
|
||||||
protected final Log logger = LogFactory.getLog(this.getClass());
|
protected final Log logger = LogFactory.getLog(this.getClass());
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -11,7 +11,7 @@ import org.springframework.security.core.Authentication;
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public final class NullAuthenticatedSessionStrategy implements AuthenticatedSessionStrategy {
|
public final class NullAuthenticatedSessionStrategy implements SessionAuthenticationStrategy {
|
||||||
|
|
||||||
public void onAuthentication(Authentication authentication, HttpServletRequest request,
|
public void onAuthentication(Authentication authentication, HttpServletRequest request,
|
||||||
HttpServletResponse response) {
|
HttpServletResponse response) {
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package org.springframework.security.web.session;
|
||||||
|
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown by an <tt>SessionAuthenticationStrategy</tt> to indicate that an authentication object is not valid for
|
||||||
|
* the current session, typically because the same user has exceeded the number of sessions they are allowed to have
|
||||||
|
* concurrently.
|
||||||
|
*
|
||||||
|
* @author Luke Taylor
|
||||||
|
* @version $Id$
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public class SessionAuthenticationException extends AuthenticationException {
|
||||||
|
|
||||||
|
public SessionAuthenticationException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows pluggable support for Http session-related behaviour when an authentication occurs.
|
* Allows pluggable support for HttpSession-related behaviour when an authentication occurs.
|
||||||
* <p>
|
* <p>
|
||||||
* Typical use would be to make sure a session exists or to change the session Id to guard against session-fixation
|
* Typical use would be to make sure a session exists or to change the session Id to guard against session-fixation
|
||||||
* attacks.
|
* attacks.
|
||||||
|
@ -16,14 +16,15 @@ import org.springframework.security.core.AuthenticationException;
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
* @since
|
* @since
|
||||||
*/
|
*/
|
||||||
public interface AuthenticatedSessionStrategy {
|
public interface SessionAuthenticationStrategy {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs Http session-related functionality when a new authentication occurs.
|
* Performs Http session-related functionality when a new authentication occurs.
|
||||||
*
|
*
|
||||||
* @throws AuthenticationException if it is decided that the authentication is not allowed for the session.
|
* @throws SessionAuthenticationException if it is decided that the authentication is not allowed for the session.
|
||||||
|
* This will typically be because the user has too many sessions open at once.
|
||||||
*/
|
*/
|
||||||
void onAuthentication(Authentication authentication, HttpServletRequest request, HttpServletResponse response)
|
void onAuthentication(Authentication authentication, HttpServletRequest request, HttpServletResponse response)
|
||||||
throws AuthenticationException;
|
throws SessionAuthenticationException;
|
||||||
|
|
||||||
}
|
}
|
|
@ -21,10 +21,8 @@ import org.springframework.web.filter.GenericFilterBean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detects that a user has been authenticated since the start of the request and, if they have, calls the
|
* Detects that a user has been authenticated since the start of the request and, if they have, calls the
|
||||||
* configured {@link AuthenticatedSessionStrategy} to perform any session-related activity (such as
|
* configured {@link SessionAuthenticationStrategy} to perform any session-related activity such as
|
||||||
* activating session-fixation protection mechanisms).
|
* activating session-fixation protection mechanisms or checking for multiple concurrent logins.
|
||||||
* <p>
|
|
||||||
* This is essentially a generalization of the functionality that was implemented for SEC-399.
|
|
||||||
*
|
*
|
||||||
* @author Martin Algesten
|
* @author Martin Algesten
|
||||||
* @author Luke Taylor
|
* @author Luke Taylor
|
||||||
|
@ -39,7 +37,7 @@ public class SessionManagementFilter extends GenericFilterBean {
|
||||||
//~ Instance fields ================================================================================================
|
//~ Instance fields ================================================================================================
|
||||||
|
|
||||||
private final SecurityContextRepository securityContextRepository;
|
private final SecurityContextRepository securityContextRepository;
|
||||||
private AuthenticatedSessionStrategy sessionStrategy = new DefaultAuthenticatedSessionStrategy();
|
private SessionAuthenticationStrategy sessionStrategy = new DefaultSessionAuthenticationStrategy();
|
||||||
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
|
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
|
||||||
private String invalidSessionUrl;
|
private String invalidSessionUrl;
|
||||||
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
|
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
|
||||||
|
@ -65,7 +63,13 @@ public class SessionManagementFilter extends GenericFilterBean {
|
||||||
|
|
||||||
if (authentication != null && !authenticationTrustResolver.isAnonymous(authentication)) {
|
if (authentication != null && !authenticationTrustResolver.isAnonymous(authentication)) {
|
||||||
// The user has been authenticated during the current request, so call the session strategy
|
// The user has been authenticated during the current request, so call the session strategy
|
||||||
sessionStrategy.onAuthentication(authentication, request, response);
|
try {
|
||||||
|
sessionStrategy.onAuthentication(authentication, request, response);
|
||||||
|
} catch (SessionAuthenticationException e) {
|
||||||
|
// The session strategy can reject the authentication
|
||||||
|
logger.debug("SessionAuthenticationStrategy rejected the authentication object",e);
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// No security context or authentication present. Check for a session timeout
|
// No security context or authentication present. Check for a session timeout
|
||||||
if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {
|
if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {
|
||||||
|
@ -83,9 +87,9 @@ public class SessionManagementFilter extends GenericFilterBean {
|
||||||
* Sets the strategy object which handles the session management behaviour when a
|
* Sets the strategy object which handles the session management behaviour when a
|
||||||
* user has been authenticated during the current request.
|
* user has been authenticated during the current request.
|
||||||
*
|
*
|
||||||
* @param sessionStrategy the strategy object. If not set, a {@link DefaultAuthenticatedSessionStrategy} is used.
|
* @param sessionStrategy the strategy object. If not set, a {@link DefaultSessionAuthenticationStrategy} is used.
|
||||||
*/
|
*/
|
||||||
public void setAuthenticatedSessionStrategy(AuthenticatedSessionStrategy sessionStrategy) {
|
public void setAuthenticatedSessionStrategy(SessionAuthenticationStrategy sessionStrategy) {
|
||||||
Assert.notNull(sessionStrategy, "authenticatedSessionStratedy must not be null");
|
Assert.notNull(sessionStrategy, "authenticatedSessionStratedy must not be null");
|
||||||
this.sessionStrategy = sessionStrategy;
|
this.sessionStrategy = sessionStrategy;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ import org.springframework.security.web.authentication.SavedRequestAwareAuthenti
|
||||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||||
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
|
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
|
||||||
import org.springframework.security.web.savedrequest.SavedRequest;
|
import org.springframework.security.web.savedrequest.SavedRequest;
|
||||||
import org.springframework.security.web.session.AuthenticatedSessionStrategy;
|
import org.springframework.security.web.session.SessionAuthenticationStrategy;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -240,7 +240,7 @@ public class AbstractProcessingFilterTests extends TestCase {
|
||||||
MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(true);
|
MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(true);
|
||||||
|
|
||||||
filter.setFilterProcessesUrl("/j_mock_post");
|
filter.setFilterProcessesUrl("/j_mock_post");
|
||||||
filter.setAuthenticatedSessionStrategy(mock(AuthenticatedSessionStrategy.class));
|
filter.setAuthenticatedSessionStrategy(mock(SessionAuthenticationStrategy.class));
|
||||||
filter.setAuthenticationSuccessHandler(successHandler);
|
filter.setAuthenticationSuccessHandler(successHandler);
|
||||||
filter.setAuthenticationFailureHandler(failureHandler);
|
filter.setAuthenticationFailureHandler(failureHandler);
|
||||||
filter.setAuthenticationManager(mock(AuthenticationManager.class));
|
filter.setAuthenticationManager(mock(AuthenticationManager.class));
|
||||||
|
|
|
@ -18,11 +18,11 @@ import org.springframework.security.web.savedrequest.SavedRequest;
|
||||||
* @author Luke Taylor
|
* @author Luke Taylor
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
public class DefaultAuthenticatedSessionStrategyTests {
|
public class DefaultSessionAuthenticationStrategyTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void newSessionShouldNotBeCreatedIfNoSessionExistsAndAlwaysCreateIsFalse() throws Exception {
|
public void newSessionShouldNotBeCreatedIfNoSessionExistsAndAlwaysCreateIsFalse() throws Exception {
|
||||||
DefaultAuthenticatedSessionStrategy strategy = new DefaultAuthenticatedSessionStrategy();
|
DefaultSessionAuthenticationStrategy strategy = new DefaultSessionAuthenticationStrategy();
|
||||||
HttpServletRequest request = new MockHttpServletRequest();
|
HttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
|
||||||
strategy.onAuthentication(mock(Authentication.class), request, new MockHttpServletResponse());
|
strategy.onAuthentication(mock(Authentication.class), request, new MockHttpServletResponse());
|
||||||
|
@ -32,7 +32,7 @@ public class DefaultAuthenticatedSessionStrategyTests {
|
||||||
|
|
||||||
// @Test
|
// @Test
|
||||||
// public void newSessionIsCreatedIfSessionAlreadyExists() throws Exception {
|
// public void newSessionIsCreatedIfSessionAlreadyExists() throws Exception {
|
||||||
// DefaultAuthenticatedSessionStrategy strategy = new DefaultAuthenticatedSessionStrategy();
|
// DefaultSessionAuthenticationStrategy strategy = new DefaultSessionAuthenticationStrategy();
|
||||||
// strategy.setSessionRegistry(mock(SessionRegistry.class));
|
// strategy.setSessionRegistry(mock(SessionRegistry.class));
|
||||||
// HttpServletRequest request = new MockHttpServletRequest();
|
// HttpServletRequest request = new MockHttpServletRequest();
|
||||||
// String sessionId = request.getSession().getId();
|
// String sessionId = request.getSession().getId();
|
||||||
|
@ -45,7 +45,7 @@ public class DefaultAuthenticatedSessionStrategyTests {
|
||||||
// See SEC-1077
|
// See SEC-1077
|
||||||
@Test
|
@Test
|
||||||
public void onlySavedRequestAttributeIsMigratedIfMigrateAttributesIsFalse() throws Exception {
|
public void onlySavedRequestAttributeIsMigratedIfMigrateAttributesIsFalse() throws Exception {
|
||||||
DefaultAuthenticatedSessionStrategy strategy = new DefaultAuthenticatedSessionStrategy();
|
DefaultSessionAuthenticationStrategy strategy = new DefaultSessionAuthenticationStrategy();
|
||||||
strategy.setMigrateSessionAttributes(false);
|
strategy.setMigrateSessionAttributes(false);
|
||||||
HttpServletRequest request = new MockHttpServletRequest();
|
HttpServletRequest request = new MockHttpServletRequest();
|
||||||
HttpSession session = request.getSession();
|
HttpSession session = request.getSession();
|
||||||
|
@ -60,7 +60,7 @@ public class DefaultAuthenticatedSessionStrategyTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void sessionIsCreatedIfAlwaysCreateTrue() throws Exception {
|
public void sessionIsCreatedIfAlwaysCreateTrue() throws Exception {
|
||||||
DefaultAuthenticatedSessionStrategy strategy = new DefaultAuthenticatedSessionStrategy();
|
DefaultSessionAuthenticationStrategy strategy = new DefaultSessionAuthenticationStrategy();
|
||||||
strategy.setAlwaysCreateSession(true);
|
strategy.setAlwaysCreateSession(true);
|
||||||
HttpServletRequest request = new MockHttpServletRequest();
|
HttpServletRequest request = new MockHttpServletRequest();
|
||||||
strategy.onAuthentication(mock(Authentication.class), request, new MockHttpServletResponse());
|
strategy.onAuthentication(mock(Authentication.class), request, new MockHttpServletResponse());
|
|
@ -44,7 +44,7 @@ public class SessionManagementFilterTests {
|
||||||
@Test
|
@Test
|
||||||
public void strategyIsNotInvokedIfSecurityContextAlreadyExistsForRequest() throws Exception {
|
public void strategyIsNotInvokedIfSecurityContextAlreadyExistsForRequest() throws Exception {
|
||||||
SecurityContextRepository repo = mock(SecurityContextRepository.class);
|
SecurityContextRepository repo = mock(SecurityContextRepository.class);
|
||||||
AuthenticatedSessionStrategy strategy = mock(AuthenticatedSessionStrategy.class);
|
SessionAuthenticationStrategy strategy = mock(SessionAuthenticationStrategy.class);
|
||||||
// mock that repo contains a security context
|
// mock that repo contains a security context
|
||||||
when(repo.containsContext(any(HttpServletRequest.class))).thenReturn(true);
|
when(repo.containsContext(any(HttpServletRequest.class))).thenReturn(true);
|
||||||
SessionManagementFilter filter = new SessionManagementFilter(repo);
|
SessionManagementFilter filter = new SessionManagementFilter(repo);
|
||||||
|
@ -60,7 +60,7 @@ public class SessionManagementFilterTests {
|
||||||
@Test
|
@Test
|
||||||
public void strategyIsNotInvokedIfAuthenticationIsNull() throws Exception {
|
public void strategyIsNotInvokedIfAuthenticationIsNull() throws Exception {
|
||||||
SecurityContextRepository repo = mock(SecurityContextRepository.class);
|
SecurityContextRepository repo = mock(SecurityContextRepository.class);
|
||||||
AuthenticatedSessionStrategy strategy = mock(AuthenticatedSessionStrategy.class);
|
SessionAuthenticationStrategy strategy = mock(SessionAuthenticationStrategy.class);
|
||||||
SessionManagementFilter filter = new SessionManagementFilter(repo);
|
SessionManagementFilter filter = new SessionManagementFilter(repo);
|
||||||
filter.setAuthenticatedSessionStrategy(strategy);
|
filter.setAuthenticatedSessionStrategy(strategy);
|
||||||
HttpServletRequest request = new MockHttpServletRequest();
|
HttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
@ -74,7 +74,7 @@ public class SessionManagementFilterTests {
|
||||||
public void strategyIsInvokedIfUserIsNewlyAuthenticated() throws Exception {
|
public void strategyIsInvokedIfUserIsNewlyAuthenticated() throws Exception {
|
||||||
SecurityContextRepository repo = mock(SecurityContextRepository.class);
|
SecurityContextRepository repo = mock(SecurityContextRepository.class);
|
||||||
// repo will return false to containsContext()
|
// repo will return false to containsContext()
|
||||||
AuthenticatedSessionStrategy strategy = mock(AuthenticatedSessionStrategy.class);
|
SessionAuthenticationStrategy strategy = mock(SessionAuthenticationStrategy.class);
|
||||||
SessionManagementFilter filter = new SessionManagementFilter(repo);
|
SessionManagementFilter filter = new SessionManagementFilter(repo);
|
||||||
filter.setAuthenticatedSessionStrategy(strategy);
|
filter.setAuthenticatedSessionStrategy(strategy);
|
||||||
HttpServletRequest request = new MockHttpServletRequest();
|
HttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
@ -92,7 +92,7 @@ public class SessionManagementFilterTests {
|
||||||
public void responseIsRedirectedToTimeoutUrlIfSetAndSessionIsInvalid() throws Exception {
|
public void responseIsRedirectedToTimeoutUrlIfSetAndSessionIsInvalid() throws Exception {
|
||||||
SecurityContextRepository repo = mock(SecurityContextRepository.class);
|
SecurityContextRepository repo = mock(SecurityContextRepository.class);
|
||||||
// repo will return false to containsContext()
|
// repo will return false to containsContext()
|
||||||
AuthenticatedSessionStrategy strategy = mock(AuthenticatedSessionStrategy.class);
|
SessionAuthenticationStrategy strategy = mock(SessionAuthenticationStrategy.class);
|
||||||
SessionManagementFilter filter = new SessionManagementFilter(repo);
|
SessionManagementFilter filter = new SessionManagementFilter(repo);
|
||||||
filter.setAuthenticatedSessionStrategy(strategy);
|
filter.setAuthenticatedSessionStrategy(strategy);
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
|
Loading…
Reference in New Issue