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:
parent
a82cab7afd
commit
b88418b94a
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -47,8 +47,10 @@ import org.springframework.security.web.context.SecurityContextRepository;
|
||||||
import org.springframework.security.web.savedrequest.NullRequestCache;
|
import org.springframework.security.web.savedrequest.NullRequestCache;
|
||||||
import org.springframework.security.web.savedrequest.RequestCache;
|
import org.springframework.security.web.savedrequest.RequestCache;
|
||||||
import org.springframework.security.web.session.ConcurrentSessionFilter;
|
import org.springframework.security.web.session.ConcurrentSessionFilter;
|
||||||
|
import org.springframework.security.web.session.ExpiredSessionStrategy;
|
||||||
import org.springframework.security.web.session.InvalidSessionStrategy;
|
import org.springframework.security.web.session.InvalidSessionStrategy;
|
||||||
import org.springframework.security.web.session.SessionManagementFilter;
|
import org.springframework.security.web.session.SessionManagementFilter;
|
||||||
|
import org.springframework.security.web.session.SimpleRedirectExpiredSessionStrategy;
|
||||||
import org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy;
|
import org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
@ -89,13 +91,14 @@ import org.springframework.util.Assert;
|
||||||
* @see SessionManagementFilter
|
* @see SessionManagementFilter
|
||||||
* @see ConcurrentSessionFilter
|
* @see ConcurrentSessionFilter
|
||||||
*/
|
*/
|
||||||
public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>> extends
|
public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
AbstractHttpConfigurer<SessionManagementConfigurer<H>, H> {
|
extends AbstractHttpConfigurer<SessionManagementConfigurer<H>, H> {
|
||||||
private final SessionAuthenticationStrategy DEFAULT_SESSION_FIXATION_STRATEGY = createDefaultSessionFixationProtectionStrategy();
|
private final SessionAuthenticationStrategy DEFAULT_SESSION_FIXATION_STRATEGY = createDefaultSessionFixationProtectionStrategy();
|
||||||
private SessionAuthenticationStrategy sessionFixationAuthenticationStrategy = DEFAULT_SESSION_FIXATION_STRATEGY;
|
private SessionAuthenticationStrategy sessionFixationAuthenticationStrategy = this.DEFAULT_SESSION_FIXATION_STRATEGY;
|
||||||
private SessionAuthenticationStrategy sessionAuthenticationStrategy;
|
private SessionAuthenticationStrategy sessionAuthenticationStrategy;
|
||||||
private SessionAuthenticationStrategy providedSessionAuthenticationStrategy;
|
private SessionAuthenticationStrategy providedSessionAuthenticationStrategy;
|
||||||
private InvalidSessionStrategy invalidSessionStrategy;
|
private InvalidSessionStrategy invalidSessionStrategy;
|
||||||
|
private ExpiredSessionStrategy expiredSessionStrategy;
|
||||||
private List<SessionAuthenticationStrategy> sessionAuthenticationStrategies = new ArrayList<SessionAuthenticationStrategy>();
|
private List<SessionAuthenticationStrategy> sessionAuthenticationStrategies = new ArrayList<SessionAuthenticationStrategy>();
|
||||||
private SessionRegistry sessionRegistry;
|
private SessionRegistry sessionRegistry;
|
||||||
private Integer maximumSessions;
|
private Integer maximumSessions;
|
||||||
|
@ -131,10 +134,12 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
* Setting this attribute will inject the provided invalidSessionStrategy into the
|
* Setting this attribute will inject the provided invalidSessionStrategy into the
|
||||||
* {@link SessionManagementFilter}. When an invalid session ID is submitted, the
|
* {@link SessionManagementFilter}. When an invalid session ID is submitted, the
|
||||||
* strategy will be invoked, redirecting to the configured URL.
|
* strategy will be invoked, redirecting to the configured URL.
|
||||||
* @param invalidSessionStrategy the strategy to use when an invalid session ID is submitted.
|
* @param invalidSessionStrategy the strategy to use when an invalid session ID is
|
||||||
|
* submitted.
|
||||||
* @return the {@link SessionManagementConfigurer} for further customization
|
* @return the {@link SessionManagementConfigurer} for further customization
|
||||||
*/
|
*/
|
||||||
public SessionManagementConfigurer<H> invalidSessionStrategy(InvalidSessionStrategy invalidSessionStrategy) {
|
public SessionManagementConfigurer<H> invalidSessionStrategy(
|
||||||
|
InvalidSessionStrategy invalidSessionStrategy) {
|
||||||
Assert.notNull(invalidSessionStrategy, "invalidSessionStrategy");
|
Assert.notNull(invalidSessionStrategy, "invalidSessionStrategy");
|
||||||
this.invalidSessionStrategy = invalidSessionStrategy;
|
this.invalidSessionStrategy = invalidSessionStrategy;
|
||||||
return this;
|
return this;
|
||||||
|
@ -198,7 +203,8 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
* {@link SessionAuthenticationStrategy} the supplied sessionAuthenticationStrategy,
|
* {@link SessionAuthenticationStrategy} the supplied sessionAuthenticationStrategy,
|
||||||
* {@link RegisterSessionAuthenticationStrategy}.
|
* {@link RegisterSessionAuthenticationStrategy}.
|
||||||
*
|
*
|
||||||
* NOTE: Supplying a custom {@link SessionAuthenticationStrategy} will override the default provided {@link SessionFixationProtectionStrategy}.
|
* NOTE: Supplying a custom {@link SessionAuthenticationStrategy} will override the
|
||||||
|
* default provided {@link SessionFixationProtectionStrategy}.
|
||||||
*
|
*
|
||||||
* @param sessionAuthenticationStrategy
|
* @param sessionAuthenticationStrategy
|
||||||
* @return the {@link SessionManagementConfigurer} for further customizations
|
* @return the {@link SessionManagementConfigurer} for further customizations
|
||||||
|
@ -244,7 +250,8 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
*/
|
*/
|
||||||
private void setSessionFixationAuthenticationStrategy(
|
private void setSessionFixationAuthenticationStrategy(
|
||||||
SessionAuthenticationStrategy sessionFixationAuthenticationStrategy) {
|
SessionAuthenticationStrategy sessionFixationAuthenticationStrategy) {
|
||||||
this.sessionFixationAuthenticationStrategy = postProcess(sessionFixationAuthenticationStrategy);
|
this.sessionFixationAuthenticationStrategy = postProcess(
|
||||||
|
sessionFixationAuthenticationStrategy);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -273,7 +280,8 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
* @return the {@link SessionManagementConfigurer} for further customizations
|
* @return the {@link SessionManagementConfigurer} for further customizations
|
||||||
*/
|
*/
|
||||||
public SessionManagementConfigurer<H> migrateSession() {
|
public SessionManagementConfigurer<H> migrateSession() {
|
||||||
setSessionFixationAuthenticationStrategy(new SessionFixationProtectionStrategy());
|
setSessionFixationAuthenticationStrategy(
|
||||||
|
new SessionFixationProtectionStrategy());
|
||||||
return SessionManagementConfigurer.this;
|
return SessionManagementConfigurer.this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,7 +296,8 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
* @throws IllegalStateException if the container is not Servlet 3.1 or newer.
|
* @throws IllegalStateException if the container is not Servlet 3.1 or newer.
|
||||||
*/
|
*/
|
||||||
public SessionManagementConfigurer<H> changeSessionId() {
|
public SessionManagementConfigurer<H> changeSessionId() {
|
||||||
setSessionFixationAuthenticationStrategy(new ChangeSessionIdAuthenticationStrategy());
|
setSessionFixationAuthenticationStrategy(
|
||||||
|
new ChangeSessionIdAuthenticationStrategy());
|
||||||
return SessionManagementConfigurer.this;
|
return SessionManagementConfigurer.this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,7 +310,8 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
* @return the {@link SessionManagementConfigurer} for further customizations
|
* @return the {@link SessionManagementConfigurer} for further customizations
|
||||||
*/
|
*/
|
||||||
public SessionManagementConfigurer<H> none() {
|
public SessionManagementConfigurer<H> none() {
|
||||||
setSessionFixationAuthenticationStrategy(new NullAuthenticatedSessionStrategy());
|
setSessionFixationAuthenticationStrategy(
|
||||||
|
new NullAuthenticatedSessionStrategy());
|
||||||
return SessionManagementConfigurer.this;
|
return SessionManagementConfigurer.this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -326,6 +336,12 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ConcurrencyControlConfigurer expiredSessionStrategy(
|
||||||
|
ExpiredSessionStrategy expiredSessionStrategy) {
|
||||||
|
SessionManagementConfigurer.this.expiredSessionStrategy = expiredSessionStrategy;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If true, prevents a user from authenticating when the
|
* If true, prevents a user from authenticating when the
|
||||||
* {@link #maximumSessions(int)} has been reached. Otherwise (default), the user
|
* {@link #maximumSessions(int)} has been reached. Otherwise (default), the user
|
||||||
|
@ -384,7 +400,8 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
HttpSessionSecurityContextRepository httpSecurityRepository = new HttpSessionSecurityContextRepository();
|
HttpSessionSecurityContextRepository httpSecurityRepository = new HttpSessionSecurityContextRepository();
|
||||||
httpSecurityRepository.setDisableUrlRewriting(!enableSessionUrlRewriting);
|
httpSecurityRepository
|
||||||
|
.setDisableUrlRewriting(!this.enableSessionUrlRewriting);
|
||||||
httpSecurityRepository.setAllowSessionCreation(isAllowSessionCreation());
|
httpSecurityRepository.setAllowSessionCreation(isAllowSessionCreation());
|
||||||
AuthenticationTrustResolver trustResolver = http
|
AuthenticationTrustResolver trustResolver = http
|
||||||
.getSharedObject(AuthenticationTrustResolver.class);
|
.getSharedObject(AuthenticationTrustResolver.class);
|
||||||
|
@ -413,15 +430,14 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
.getSharedObject(SecurityContextRepository.class);
|
.getSharedObject(SecurityContextRepository.class);
|
||||||
SessionManagementFilter sessionManagementFilter = new SessionManagementFilter(
|
SessionManagementFilter sessionManagementFilter = new SessionManagementFilter(
|
||||||
securityContextRepository, getSessionAuthenticationStrategy(http));
|
securityContextRepository, getSessionAuthenticationStrategy(http));
|
||||||
if (sessionAuthenticationErrorUrl != null) {
|
if (this.sessionAuthenticationErrorUrl != null) {
|
||||||
sessionManagementFilter
|
sessionManagementFilter.setAuthenticationFailureHandler(
|
||||||
.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler(
|
new SimpleUrlAuthenticationFailureHandler(
|
||||||
sessionAuthenticationErrorUrl));
|
this.sessionAuthenticationErrorUrl));
|
||||||
}
|
}
|
||||||
InvalidSessionStrategy strategy = getInvalidSessionStrategy();
|
InvalidSessionStrategy strategy = getInvalidSessionStrategy();
|
||||||
if (strategy != null) {
|
if (strategy != null) {
|
||||||
sessionManagementFilter
|
sessionManagementFilter.setInvalidSessionStrategy(strategy);
|
||||||
.setInvalidSessionStrategy(strategy);
|
|
||||||
}
|
}
|
||||||
AuthenticationTrustResolver trustResolver = http
|
AuthenticationTrustResolver trustResolver = http
|
||||||
.getSharedObject(AuthenticationTrustResolver.class);
|
.getSharedObject(AuthenticationTrustResolver.class);
|
||||||
|
@ -433,7 +449,10 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
http.addFilter(sessionManagementFilter);
|
http.addFilter(sessionManagementFilter);
|
||||||
if (isConcurrentSessionControlEnabled()) {
|
if (isConcurrentSessionControlEnabled()) {
|
||||||
ConcurrentSessionFilter concurrentSessionFilter = new ConcurrentSessionFilter(
|
ConcurrentSessionFilter concurrentSessionFilter = new ConcurrentSessionFilter(
|
||||||
getSessionRegistry(http), expiredUrl);
|
getSessionRegistry(http));
|
||||||
|
concurrentSessionFilter
|
||||||
|
.setExpiredSessionStrategy(getExpiredSessionStrategy());
|
||||||
|
|
||||||
concurrentSessionFilter = postProcess(concurrentSessionFilter);
|
concurrentSessionFilter = postProcess(concurrentSessionFilter);
|
||||||
http.addFilter(concurrentSessionFilter);
|
http.addFilter(concurrentSessionFilter);
|
||||||
}
|
}
|
||||||
|
@ -447,14 +466,30 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
* @return the {@link InvalidSessionStrategy} to use
|
* @return the {@link InvalidSessionStrategy} to use
|
||||||
*/
|
*/
|
||||||
InvalidSessionStrategy getInvalidSessionStrategy() {
|
InvalidSessionStrategy getInvalidSessionStrategy() {
|
||||||
if(invalidSessionStrategy != null) {
|
if (this.invalidSessionStrategy != null) {
|
||||||
return invalidSessionStrategy;
|
return this.invalidSessionStrategy;
|
||||||
}
|
}
|
||||||
if (invalidSessionUrl != null) {
|
if (this.invalidSessionUrl != null) {
|
||||||
invalidSessionStrategy = new SimpleRedirectInvalidSessionStrategy(
|
this.invalidSessionStrategy = new SimpleRedirectInvalidSessionStrategy(
|
||||||
invalidSessionUrl);
|
this.invalidSessionUrl);
|
||||||
}
|
}
|
||||||
return invalidSessionStrategy;
|
return this.invalidSessionStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpiredSessionStrategy getExpiredSessionStrategy() {
|
||||||
|
if (this.expiredSessionStrategy != null) {
|
||||||
|
return this.expiredSessionStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.expiredUrl == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.expiredSessionStrategy == null) {
|
||||||
|
this.expiredSessionStrategy = new SimpleRedirectExpiredSessionStrategy(
|
||||||
|
this.expiredUrl);
|
||||||
|
}
|
||||||
|
return this.expiredSessionStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -462,7 +497,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
* @return the {@link SessionCreationPolicy}
|
* @return the {@link SessionCreationPolicy}
|
||||||
*/
|
*/
|
||||||
SessionCreationPolicy getSessionCreationPolicy() {
|
SessionCreationPolicy getSessionCreationPolicy() {
|
||||||
return sessionPolicy;
|
return this.sessionPolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -471,8 +506,8 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
* @return true if the {@link SessionCreationPolicy} allows session creation
|
* @return true if the {@link SessionCreationPolicy} allows session creation
|
||||||
*/
|
*/
|
||||||
private boolean isAllowSessionCreation() {
|
private boolean isAllowSessionCreation() {
|
||||||
return SessionCreationPolicy.ALWAYS == sessionPolicy
|
return SessionCreationPolicy.ALWAYS == this.sessionPolicy
|
||||||
|| SessionCreationPolicy.IF_REQUIRED == sessionPolicy;
|
|| SessionCreationPolicy.IF_REQUIRED == this.sessionPolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -480,7 +515,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private boolean isStateless() {
|
private boolean isStateless() {
|
||||||
return SessionCreationPolicy.STATELESS == sessionPolicy;
|
return SessionCreationPolicy.STATELESS == this.sessionPolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -491,50 +526,52 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
* @return the {@link SessionAuthenticationStrategy} to use
|
* @return the {@link SessionAuthenticationStrategy} to use
|
||||||
*/
|
*/
|
||||||
private SessionAuthenticationStrategy getSessionAuthenticationStrategy(H http) {
|
private SessionAuthenticationStrategy getSessionAuthenticationStrategy(H http) {
|
||||||
if (sessionAuthenticationStrategy != null) {
|
if (this.sessionAuthenticationStrategy != null) {
|
||||||
return sessionAuthenticationStrategy;
|
return this.sessionAuthenticationStrategy;
|
||||||
}
|
}
|
||||||
List<SessionAuthenticationStrategy> delegateStrategies = sessionAuthenticationStrategies;
|
List<SessionAuthenticationStrategy> delegateStrategies = this.sessionAuthenticationStrategies;
|
||||||
SessionAuthenticationStrategy defaultSessionAuthenticationStrategy;
|
SessionAuthenticationStrategy defaultSessionAuthenticationStrategy;
|
||||||
if (providedSessionAuthenticationStrategy == null) {
|
if (this.providedSessionAuthenticationStrategy == null) {
|
||||||
// If a user provided SessionAuthenticationStrategy is not supplied
|
// If a user provided SessionAuthenticationStrategy is not supplied
|
||||||
// then default to SessionFixationProtectionStrategy
|
// then default to SessionFixationProtectionStrategy
|
||||||
defaultSessionAuthenticationStrategy = postProcess(sessionFixationAuthenticationStrategy);
|
defaultSessionAuthenticationStrategy = postProcess(
|
||||||
} else {
|
this.sessionFixationAuthenticationStrategy);
|
||||||
defaultSessionAuthenticationStrategy = providedSessionAuthenticationStrategy;
|
}
|
||||||
|
else {
|
||||||
|
defaultSessionAuthenticationStrategy = this.providedSessionAuthenticationStrategy;
|
||||||
}
|
}
|
||||||
if (isConcurrentSessionControlEnabled()) {
|
if (isConcurrentSessionControlEnabled()) {
|
||||||
SessionRegistry sessionRegistry = getSessionRegistry(http);
|
SessionRegistry sessionRegistry = getSessionRegistry(http);
|
||||||
ConcurrentSessionControlAuthenticationStrategy concurrentSessionControlStrategy = new ConcurrentSessionControlAuthenticationStrategy(
|
ConcurrentSessionControlAuthenticationStrategy concurrentSessionControlStrategy = new ConcurrentSessionControlAuthenticationStrategy(
|
||||||
sessionRegistry);
|
sessionRegistry);
|
||||||
concurrentSessionControlStrategy.setMaximumSessions(maximumSessions);
|
concurrentSessionControlStrategy.setMaximumSessions(this.maximumSessions);
|
||||||
concurrentSessionControlStrategy
|
concurrentSessionControlStrategy
|
||||||
.setExceptionIfMaximumExceeded(maxSessionsPreventsLogin);
|
.setExceptionIfMaximumExceeded(this.maxSessionsPreventsLogin);
|
||||||
concurrentSessionControlStrategy = postProcess(concurrentSessionControlStrategy);
|
concurrentSessionControlStrategy = postProcess(
|
||||||
|
concurrentSessionControlStrategy);
|
||||||
|
|
||||||
RegisterSessionAuthenticationStrategy registerSessionStrategy = new RegisterSessionAuthenticationStrategy(
|
RegisterSessionAuthenticationStrategy registerSessionStrategy = new RegisterSessionAuthenticationStrategy(
|
||||||
sessionRegistry);
|
sessionRegistry);
|
||||||
registerSessionStrategy = postProcess(registerSessionStrategy);
|
registerSessionStrategy = postProcess(registerSessionStrategy);
|
||||||
|
|
||||||
delegateStrategies.addAll(Arrays.asList(
|
delegateStrategies.addAll(Arrays.asList(concurrentSessionControlStrategy,
|
||||||
concurrentSessionControlStrategy,
|
defaultSessionAuthenticationStrategy, registerSessionStrategy));
|
||||||
defaultSessionAuthenticationStrategy,
|
}
|
||||||
registerSessionStrategy));
|
else {
|
||||||
} else {
|
|
||||||
delegateStrategies.add(defaultSessionAuthenticationStrategy);
|
delegateStrategies.add(defaultSessionAuthenticationStrategy);
|
||||||
}
|
}
|
||||||
sessionAuthenticationStrategy = postProcess(new CompositeSessionAuthenticationStrategy(
|
this.sessionAuthenticationStrategy = postProcess(
|
||||||
delegateStrategies));
|
new CompositeSessionAuthenticationStrategy(delegateStrategies));
|
||||||
return sessionAuthenticationStrategy;
|
return this.sessionAuthenticationStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SessionRegistry getSessionRegistry(H http) {
|
private SessionRegistry getSessionRegistry(H http) {
|
||||||
if (sessionRegistry == null) {
|
if (this.sessionRegistry == null) {
|
||||||
SessionRegistryImpl sessionRegistry = new SessionRegistryImpl();
|
SessionRegistryImpl sessionRegistry = new SessionRegistryImpl();
|
||||||
registerDelegateApplicationListener(http, sessionRegistry);
|
registerDelegateApplicationListener(http, sessionRegistry);
|
||||||
this.sessionRegistry = sessionRegistry;
|
this.sessionRegistry = sessionRegistry;
|
||||||
}
|
}
|
||||||
return sessionRegistry;
|
return this.sessionRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerDelegateApplicationListener(H http,
|
private void registerDelegateApplicationListener(H http,
|
||||||
|
@ -558,7 +595,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private boolean isConcurrentSessionControlEnabled() {
|
private boolean isConcurrentSessionControlEnabled() {
|
||||||
return maximumSessions != null;
|
return this.maximumSessions != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -15,13 +15,9 @@
|
||||||
*/
|
*/
|
||||||
package org.springframework.security.config.http;
|
package org.springframework.security.config.http;
|
||||||
|
|
||||||
import static org.springframework.security.config.http.HttpSecurityBeanDefinitionParser.*;
|
|
||||||
import static org.springframework.security.config.http.SecurityFilters.*;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.servlet.ServletRequest;
|
import javax.servlet.ServletRequest;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
@ -69,6 +65,7 @@ import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
|
||||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
|
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
|
||||||
import org.springframework.security.web.session.ConcurrentSessionFilter;
|
import org.springframework.security.web.session.ConcurrentSessionFilter;
|
||||||
import org.springframework.security.web.session.SessionManagementFilter;
|
import org.springframework.security.web.session.SessionManagementFilter;
|
||||||
|
import org.springframework.security.web.session.SimpleRedirectExpiredSessionStrategy;
|
||||||
import org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy;
|
import org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy;
|
||||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
@ -77,6 +74,9 @@ import org.springframework.util.StringUtils;
|
||||||
import org.springframework.util.xml.DomUtils;
|
import org.springframework.util.xml.DomUtils;
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
|
|
||||||
|
import static org.springframework.security.config.http.HttpSecurityBeanDefinitionParser.*;
|
||||||
|
import static org.springframework.security.config.http.SecurityFilters.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stateful class which helps HttpSecurityBDP to create the configuration for the
|
* Stateful class which helps HttpSecurityBDP to create the configuration for the
|
||||||
* <http> element.
|
* <http> element.
|
||||||
|
@ -97,7 +97,7 @@ class HttpConfigurationBuilder {
|
||||||
private static final String ATT_SESSION_AUTH_STRATEGY_REF = "session-authentication-strategy-ref";
|
private static final String ATT_SESSION_AUTH_STRATEGY_REF = "session-authentication-strategy-ref";
|
||||||
private static final String ATT_SESSION_AUTH_ERROR_URL = "session-authentication-error-url";
|
private static final String ATT_SESSION_AUTH_ERROR_URL = "session-authentication-error-url";
|
||||||
private static final String ATT_SECURITY_CONTEXT_REPOSITORY = "security-context-repository-ref";
|
private static final String ATT_SECURITY_CONTEXT_REPOSITORY = "security-context-repository-ref";
|
||||||
|
private static final String ATT_INVALID_SESSION_STRATEGY_REF = "invalid-session-strategy-ref";
|
||||||
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_ACCESS_MGR = "access-decision-manager-ref";
|
private static final String ATT_ACCESS_MGR = "access-decision-manager-ref";
|
||||||
|
@ -289,6 +289,7 @@ class HttpConfigurationBuilder {
|
||||||
|
|
||||||
String sessionFixationAttribute = null;
|
String sessionFixationAttribute = null;
|
||||||
String invalidSessionUrl = null;
|
String invalidSessionUrl = null;
|
||||||
|
String invalidSessionStrategyRef = null;
|
||||||
String sessionAuthStratRef = null;
|
String sessionAuthStratRef = null;
|
||||||
String errorUrl = null;
|
String errorUrl = null;
|
||||||
|
|
||||||
|
@ -304,6 +305,8 @@ class HttpConfigurationBuilder {
|
||||||
sessionFixationAttribute = sessionMgmtElt
|
sessionFixationAttribute = sessionMgmtElt
|
||||||
.getAttribute(ATT_SESSION_FIXATION_PROTECTION);
|
.getAttribute(ATT_SESSION_FIXATION_PROTECTION);
|
||||||
invalidSessionUrl = sessionMgmtElt.getAttribute(ATT_INVALID_SESSION_URL);
|
invalidSessionUrl = sessionMgmtElt.getAttribute(ATT_INVALID_SESSION_URL);
|
||||||
|
invalidSessionStrategyRef = sessionMgmtElt.getAttribute(ATT_INVALID_SESSION_STRATEGY_REF);
|
||||||
|
|
||||||
sessionAuthStratRef = sessionMgmtElt
|
sessionAuthStratRef = sessionMgmtElt
|
||||||
.getAttribute(ATT_SESSION_AUTH_STRATEGY_REF);
|
.getAttribute(ATT_SESSION_AUTH_STRATEGY_REF);
|
||||||
errorUrl = sessionMgmtElt.getAttribute(ATT_SESSION_AUTH_ERROR_URL);
|
errorUrl = sessionMgmtElt.getAttribute(ATT_SESSION_AUTH_ERROR_URL);
|
||||||
|
@ -311,6 +314,11 @@ class HttpConfigurationBuilder {
|
||||||
Elements.CONCURRENT_SESSIONS);
|
Elements.CONCURRENT_SESSIONS);
|
||||||
sessionControlEnabled = sessionCtrlElt != null;
|
sessionControlEnabled = sessionCtrlElt != null;
|
||||||
|
|
||||||
|
if (StringUtils.hasText(invalidSessionUrl) && StringUtils.hasText(invalidSessionStrategyRef)) {
|
||||||
|
pc.getReaderContext().error(ATT_INVALID_SESSION_URL + " attribute cannot be used in combination with" +
|
||||||
|
" the " + ATT_INVALID_SESSION_STRATEGY_REF + " attribute.", sessionMgmtElt);
|
||||||
|
}
|
||||||
|
|
||||||
if (sessionControlEnabled) {
|
if (sessionControlEnabled) {
|
||||||
if (StringUtils.hasText(sessionAuthStratRef)) {
|
if (StringUtils.hasText(sessionAuthStratRef)) {
|
||||||
pc.getReaderContext().error(
|
pc.getReaderContext().error(
|
||||||
|
@ -438,12 +446,16 @@ class HttpConfigurationBuilder {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (StringUtils.hasText(invalidSessionUrl)) {
|
if (StringUtils.hasText(invalidSessionUrl)) {
|
||||||
BeanDefinitionBuilder invalidSessionBldr = BeanDefinitionBuilder
|
BeanDefinitionBuilder invalidSessionBldr = BeanDefinitionBuilder
|
||||||
.rootBeanDefinition(SimpleRedirectInvalidSessionStrategy.class);
|
.rootBeanDefinition(SimpleRedirectInvalidSessionStrategy.class);
|
||||||
invalidSessionBldr.addConstructorArgValue(invalidSessionUrl);
|
invalidSessionBldr.addConstructorArgValue(invalidSessionUrl);
|
||||||
invalidSession = invalidSessionBldr.getBeanDefinition();
|
invalidSession = invalidSessionBldr.getBeanDefinition();
|
||||||
sessionMgmtFilter.addPropertyValue("invalidSessionStrategy", invalidSession);
|
sessionMgmtFilter.addPropertyValue("invalidSessionStrategy", invalidSession);
|
||||||
|
} else if (StringUtils.hasText(invalidSessionStrategyRef)) {
|
||||||
|
sessionMgmtFilter.addPropertyReference("invalidSessionStrategy", invalidSessionStrategyRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionMgmtFilter.addConstructorArgReference(sessionAuthStratRef);
|
sessionMgmtFilter.addConstructorArgReference(sessionAuthStratRef);
|
||||||
|
@ -454,6 +466,7 @@ class HttpConfigurationBuilder {
|
||||||
|
|
||||||
private void createConcurrencyControlFilterAndSessionRegistry(Element element) {
|
private void createConcurrencyControlFilterAndSessionRegistry(Element element) {
|
||||||
final String ATT_EXPIRY_URL = "expired-url";
|
final String ATT_EXPIRY_URL = "expired-url";
|
||||||
|
final String ATT_EXPIRED_SESSION_STRATEGY_REF = "expired-session-strategy-ref";
|
||||||
final String ATT_SESSION_REGISTRY_ALIAS = "session-registry-alias";
|
final String ATT_SESSION_REGISTRY_ALIAS = "session-registry-alias";
|
||||||
final String ATT_SESSION_REGISTRY_REF = "session-registry-ref";
|
final String ATT_SESSION_REGISTRY_REF = "session-registry-ref";
|
||||||
|
|
||||||
|
@ -489,10 +502,20 @@ class HttpConfigurationBuilder {
|
||||||
filterBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
filterBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||||
|
|
||||||
String expiryUrl = element.getAttribute(ATT_EXPIRY_URL);
|
String expiryUrl = element.getAttribute(ATT_EXPIRY_URL);
|
||||||
|
String expiredSessionStrategyRef = element.getAttribute(ATT_EXPIRED_SESSION_STRATEGY_REF);
|
||||||
|
|
||||||
|
if (StringUtils.hasText(expiryUrl) && StringUtils.hasText(expiredSessionStrategyRef)) {
|
||||||
|
pc.getReaderContext().error("Cannot use 'expired-url' attribute and 'expired-session-strategy-ref'" +
|
||||||
|
" attribute together.", source);
|
||||||
|
}
|
||||||
|
|
||||||
if (StringUtils.hasText(expiryUrl)) {
|
if (StringUtils.hasText(expiryUrl)) {
|
||||||
WebConfigUtils.validateHttpRedirect(expiryUrl, pc, source);
|
BeanDefinitionBuilder expiredSessionBldr = BeanDefinitionBuilder
|
||||||
filterBuilder.addConstructorArgValue(expiryUrl);
|
.rootBeanDefinition(SimpleRedirectExpiredSessionStrategy.class);
|
||||||
|
expiredSessionBldr.addConstructorArgValue(expiryUrl);
|
||||||
|
filterBuilder.addPropertyValue("expiredSessionStrategy", expiredSessionBldr.getBeanDefinition());
|
||||||
|
} else if (StringUtils.hasText(expiredSessionStrategyRef)) {
|
||||||
|
filterBuilder.addPropertyReference("expiredSessionStrategy", expiredSessionStrategyRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
pc.popAndRegisterContainingComponent();
|
pc.popAndRegisterContainingComponent();
|
||||||
|
|
|
@ -535,6 +535,9 @@ session-management.attlist &=
|
||||||
session-management.attlist &=
|
session-management.attlist &=
|
||||||
## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.
|
## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.
|
||||||
attribute invalid-session-url {xsd:token}?
|
attribute invalid-session-url {xsd:token}?
|
||||||
|
session-management.attlist &=
|
||||||
|
## Allows injection of the InvalidSessionStrategy instance used by the SessionManagementFilter
|
||||||
|
attribute invalid-session-strategy-ref {xsd:token}?
|
||||||
session-management.attlist &=
|
session-management.attlist &=
|
||||||
## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter
|
## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter
|
||||||
attribute session-authentication-strategy-ref {xsd:token}?
|
attribute session-authentication-strategy-ref {xsd:token}?
|
||||||
|
@ -553,6 +556,9 @@ concurrency-control.attlist &=
|
||||||
concurrency-control.attlist &=
|
concurrency-control.attlist &=
|
||||||
## The URL a user will be redirected to if they attempt to use a session which has been "expired" because they have logged in again.
|
## The URL a user will be redirected to if they attempt to use a session which has been "expired" because they have logged in again.
|
||||||
attribute expired-url {xsd:token}?
|
attribute expired-url {xsd:token}?
|
||||||
|
concurrency-control.attlist &=
|
||||||
|
## Allows injection of the ExpiredSessionStrategy instance used by the ConcurrentSessionFilter
|
||||||
|
attribute expired-session-strategy-ref {xsd:token}?
|
||||||
concurrency-control.attlist &=
|
concurrency-control.attlist &=
|
||||||
## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.
|
## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.
|
||||||
attribute error-if-maximum-exceeded {xsd:boolean}?
|
attribute error-if-maximum-exceeded {xsd:boolean}?
|
||||||
|
|
|
@ -1743,6 +1743,13 @@
|
||||||
</xs:documentation>
|
</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
|
<xs:attribute name="invalid-session-strategy-ref" type="xs:token">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Allows injection of the InvalidSessionStrategy instance used by the
|
||||||
|
SessionManagementFilter
|
||||||
|
</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
<xs:attribute name="session-authentication-strategy-ref" type="xs:token">
|
<xs:attribute name="session-authentication-strategy-ref" type="xs:token">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the
|
<xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the
|
||||||
|
@ -1777,6 +1784,13 @@
|
||||||
</xs:documentation>
|
</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
|
<xs:attribute name="expired-session-strategy-ref" type="xs:token">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Allows injection of the ExpiredSessionStrategy instance used by the
|
||||||
|
ConcurrentSessionFilter
|
||||||
|
</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
<xs:attribute name="error-if-maximum-exceeded" type="xs:boolean">
|
<xs:attribute name="error-if-maximum-exceeded" type="xs:boolean">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when
|
<xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when
|
||||||
|
|
|
@ -67,7 +67,7 @@ class NamespaceSessionManagementTests extends BaseSpringSpec {
|
||||||
concurrentStrategy.maximumSessions == 1
|
concurrentStrategy.maximumSessions == 1
|
||||||
concurrentStrategy.exceptionIfMaximumExceeded
|
concurrentStrategy.exceptionIfMaximumExceeded
|
||||||
concurrentStrategy.sessionRegistry == CustomSessionManagementConfig.SR
|
concurrentStrategy.sessionRegistry == CustomSessionManagementConfig.SR
|
||||||
findFilter(ConcurrentSessionFilter).expiredUrl == "/expired-session"
|
findFilter(ConcurrentSessionFilter).expiredSessionStrategy.destinationUrl == "/expired-session"
|
||||||
}
|
}
|
||||||
|
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
|
|
|
@ -15,13 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package org.springframework.security.config.http
|
package org.springframework.security.config.http
|
||||||
|
|
||||||
import static org.junit.Assert.assertSame
|
|
||||||
import static org.mockito.Mockito.*
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest
|
|
||||||
import javax.servlet.http.HttpServletResponse
|
|
||||||
|
|
||||||
import org.mockito.Mockito
|
|
||||||
import org.springframework.mock.web.MockFilterChain
|
import org.springframework.mock.web.MockFilterChain
|
||||||
import org.springframework.mock.web.MockHttpServletRequest
|
import org.springframework.mock.web.MockHttpServletRequest
|
||||||
import org.springframework.mock.web.MockHttpServletResponse
|
import org.springframework.mock.web.MockHttpServletResponse
|
||||||
|
@ -41,7 +34,6 @@ import org.springframework.security.web.authentication.logout.CookieClearingLogo
|
||||||
import org.springframework.security.web.authentication.logout.LogoutFilter
|
import org.springframework.security.web.authentication.logout.LogoutFilter
|
||||||
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler
|
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler
|
||||||
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter
|
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter
|
||||||
import org.springframework.security.web.authentication.session.SessionAuthenticationException
|
|
||||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy
|
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy
|
||||||
import org.springframework.security.web.context.NullSecurityContextRepository
|
import org.springframework.security.web.context.NullSecurityContextRepository
|
||||||
import org.springframework.security.web.context.SaveContextOnUpdateOrErrorResponseWrapper
|
import org.springframework.security.web.context.SaveContextOnUpdateOrErrorResponseWrapper
|
||||||
|
@ -50,6 +42,13 @@ import org.springframework.security.web.savedrequest.RequestCacheAwareFilter
|
||||||
import org.springframework.security.web.session.ConcurrentSessionFilter
|
import org.springframework.security.web.session.ConcurrentSessionFilter
|
||||||
import org.springframework.security.web.session.SessionManagementFilter
|
import org.springframework.security.web.session.SessionManagementFilter
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest
|
||||||
|
import javax.servlet.http.HttpServletResponse
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertSame
|
||||||
|
import static org.mockito.Matchers.any
|
||||||
|
import static org.mockito.Mockito.verify
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests session-related functionality for the <http> namespace element and <session-management>
|
* Tests session-related functionality for the <http> namespace element and <session-management>
|
||||||
*
|
*
|
||||||
|
@ -164,7 +163,7 @@ class SessionManagementConfigTests extends AbstractHttpConfigTests {
|
||||||
|
|
||||||
then:
|
then:
|
||||||
concurrentSessionFilter instanceof ConcurrentSessionFilter
|
concurrentSessionFilter instanceof ConcurrentSessionFilter
|
||||||
concurrentSessionFilter.expiredUrl == '/expired'
|
concurrentSessionFilter.expiredSessionStrategy.destinationUrl == '/expired'
|
||||||
appContext.getBean("sr") != null
|
appContext.getBean("sr") != null
|
||||||
getFilter(SessionManagementFilter.class) != null
|
getFilter(SessionManagementFilter.class) != null
|
||||||
sessionRegistryIsValid();
|
sessionRegistryIsValid();
|
||||||
|
@ -271,7 +270,7 @@ class SessionManagementConfigTests extends AbstractHttpConfigTests {
|
||||||
List filters = getFilters("/someurl");
|
List filters = getFilters("/someurl");
|
||||||
|
|
||||||
expect:
|
expect:
|
||||||
filters.get(1).expiredUrl == null
|
filters.get(1).expiredSessionStrategy == null
|
||||||
}
|
}
|
||||||
|
|
||||||
def externalSessionStrategyIsSupported() {
|
def externalSessionStrategyIsSupported() {
|
||||||
|
|
|
@ -8592,6 +8592,9 @@ Session-management related functionality is implemented by the addition of a `Se
|
||||||
* **invalid-session-url**
|
* **invalid-session-url**
|
||||||
Setting this attribute will inject the `SessionManagementFilter` with a `SimpleRedirectInvalidSessionStrategy` configured with the attribute value. When an invalid session ID is submitted, the strategy will be invoked, redirecting to the configured URL.
|
Setting this attribute will inject the `SessionManagementFilter` with a `SimpleRedirectInvalidSessionStrategy` configured with the attribute value. When an invalid session ID is submitted, the strategy will be invoked, redirecting to the configured URL.
|
||||||
|
|
||||||
|
[[nsa-session-management-invalid-session-strategy-ref]]
|
||||||
|
* **invalid-session-url**
|
||||||
|
Allows injection of the InvalidSessionStrategy instance used by the SessionManagementFilter. Use either this or the `invalid-session-url` attribute but not both.
|
||||||
|
|
||||||
[[nsa-session-management-session-authentication-error-url]]
|
[[nsa-session-management-session-authentication-error-url]]
|
||||||
* **session-authentication-error-url**
|
* **session-authentication-error-url**
|
||||||
|
@ -8646,6 +8649,9 @@ If set to "true" a `SessionAuthenticationException` will be raised when a user a
|
||||||
* **expired-url**
|
* **expired-url**
|
||||||
The URL a user will be redirected to if they attempt to use a session which has been "expired" by the concurrent session controller because the user has exceeded the number of allowed sessions and has logged in again elsewhere. Should be set unless `exception-if-maximum-exceeded` is set. If no value is supplied, an expiry message will just be written directly back to the response.
|
The URL a user will be redirected to if they attempt to use a session which has been "expired" by the concurrent session controller because the user has exceeded the number of allowed sessions and has logged in again elsewhere. Should be set unless `exception-if-maximum-exceeded` is set. If no value is supplied, an expiry message will just be written directly back to the response.
|
||||||
|
|
||||||
|
[[nsa-concurrency-control-expired-session-strategy-ref]]
|
||||||
|
* **expired-url**
|
||||||
|
Allows injection of the ExpiredSessionStrategy instance used by the ConcurrentSessionFilter
|
||||||
|
|
||||||
[[nsa-concurrency-control-max-sessions]]
|
[[nsa-concurrency-control-max-sessions]]
|
||||||
* **max-sessions**
|
* **max-sessions**
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package org.springframework.security.web.session;
|
package org.springframework.security.web.session;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import javax.servlet.FilterChain;
|
import javax.servlet.FilterChain;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.ServletRequest;
|
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.CompositeLogoutHandler;
|
||||||
import org.springframework.security.web.authentication.logout.LogoutHandler;
|
import org.springframework.security.web.authentication.logout.LogoutHandler;
|
||||||
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
||||||
import org.springframework.security.web.util.UrlUtils;
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.web.filter.GenericFilterBean;
|
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
|
* as expired. If it has been marked as expired, the configured logout handlers will be
|
||||||
* called (as happens with
|
* called (as happens with
|
||||||
* {@link org.springframework.security.web.authentication.logout.LogoutFilter}), typically
|
* {@link org.springframework.security.web.authentication.logout.LogoutFilter}), typically
|
||||||
* to invalidate the session. A redirect to the expiredURL specified will be performed,
|
* to invalidate the session. To handle the expired session a call to the {@link ExpiredSessionStrategy} is made.
|
||||||
* and the session invalidation will cause an
|
* The session invalidation will cause an
|
||||||
* {@link org.springframework.security.web.session.HttpSessionDestroyedEvent} to be
|
* {@link org.springframework.security.web.session.HttpSessionDestroyedEvent} to be
|
||||||
* published via the
|
* published via the
|
||||||
* {@link org.springframework.security.web.session.HttpSessionEventPublisher} registered
|
* {@link org.springframework.security.web.session.HttpSessionEventPublisher} registered
|
||||||
|
@ -61,15 +59,15 @@ import org.springframework.web.filter.GenericFilterBean;
|
||||||
*
|
*
|
||||||
* @author Ben Alex
|
* @author Ben Alex
|
||||||
* @author Eddú Meléndez
|
* @author Eddú Meléndez
|
||||||
|
* @author Marten Deinum
|
||||||
*/
|
*/
|
||||||
public class ConcurrentSessionFilter extends GenericFilterBean {
|
public class ConcurrentSessionFilter extends GenericFilterBean {
|
||||||
// ~ Instance fields
|
// ~ Instance fields
|
||||||
// ================================================================================================
|
// ================================================================================================
|
||||||
|
|
||||||
private SessionRegistry sessionRegistry;
|
private final SessionRegistry sessionRegistry;
|
||||||
private String expiredUrl;
|
|
||||||
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
|
|
||||||
private LogoutHandler handlers = new CompositeLogoutHandler(new SecurityContextLogoutHandler());
|
private LogoutHandler handlers = new CompositeLogoutHandler(new SecurityContextLogoutHandler());
|
||||||
|
private ExpiredSessionStrategy expiredSessionStrategy;
|
||||||
|
|
||||||
// ~ Methods
|
// ~ Methods
|
||||||
// ========================================================================================================
|
// ========================================================================================================
|
||||||
|
@ -81,17 +79,13 @@ public class ConcurrentSessionFilter extends GenericFilterBean {
|
||||||
|
|
||||||
public ConcurrentSessionFilter(SessionRegistry sessionRegistry, String expiredUrl) {
|
public ConcurrentSessionFilter(SessionRegistry sessionRegistry, String expiredUrl) {
|
||||||
Assert.notNull(sessionRegistry, "SessionRegistry required");
|
Assert.notNull(sessionRegistry, "SessionRegistry required");
|
||||||
Assert.isTrue(expiredUrl == null || UrlUtils.isValidRedirectUrl(expiredUrl),
|
|
||||||
expiredUrl + " isn't a valid redirect URL");
|
|
||||||
this.sessionRegistry = sessionRegistry;
|
this.sessionRegistry = sessionRegistry;
|
||||||
this.expiredUrl = expiredUrl;
|
this.expiredSessionStrategy = new SimpleRedirectExpiredSessionStrategy(expiredUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterPropertiesSet() {
|
public void afterPropertiesSet() {
|
||||||
Assert.notNull(sessionRegistry, "SessionRegistry required");
|
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)
|
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
|
||||||
|
@ -108,12 +102,14 @@ public class ConcurrentSessionFilter extends GenericFilterBean {
|
||||||
if (info != null) {
|
if (info != null) {
|
||||||
if (info.isExpired()) {
|
if (info.isExpired()) {
|
||||||
// Expired - abort processing
|
// Expired - abort processing
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Requested session ID "
|
||||||
|
+ request.getRequestedSessionId() + " has expired.");
|
||||||
|
}
|
||||||
doLogout(request, response);
|
doLogout(request, response);
|
||||||
|
|
||||||
String targetUrl = determineExpiredUrl(request, info);
|
if (this.expiredSessionStrategy != null) {
|
||||||
|
this.expiredSessionStrategy.onExpiredSessionDetected(request, response);
|
||||||
if (targetUrl != null) {
|
|
||||||
redirectStrategy.sendRedirect(request, response, targetUrl);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -136,10 +132,6 @@ public class ConcurrentSessionFilter extends GenericFilterBean {
|
||||||
chain.doFilter(request, response);
|
chain.doFilter(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String determineExpiredUrl(HttpServletRequest request,
|
|
||||||
SessionInformation info) {
|
|
||||||
return expiredUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doLogout(HttpServletRequest request, HttpServletResponse response) {
|
private void doLogout(HttpServletRequest request, HttpServletResponse response) {
|
||||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
@ -151,7 +143,7 @@ public class ConcurrentSessionFilter extends GenericFilterBean {
|
||||||
this.handlers = new CompositeLogoutHandler(handlers);
|
this.handlers = new CompositeLogoutHandler(handlers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
|
public void setExpiredSessionStrategy(ExpiredSessionStrategy expiredSessionStrategy) {
|
||||||
this.redirectStrategy = redirectStrategy;
|
this.expiredSessionStrategy=expiredSessionStrategy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -16,11 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.security.web.concurrent;
|
package org.springframework.security.web.concurrent;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.*;
|
|
||||||
import static org.mockito.Mockito.*;
|
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import javax.servlet.FilterChain;
|
import javax.servlet.FilterChain;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -29,10 +25,13 @@ import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
import org.springframework.mock.web.MockHttpSession;
|
import org.springframework.mock.web.MockHttpSession;
|
||||||
import org.springframework.security.core.session.SessionRegistry;
|
import org.springframework.security.core.session.SessionRegistry;
|
||||||
import org.springframework.security.core.session.SessionRegistryImpl;
|
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.LogoutHandler;
|
||||||
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
||||||
import org.springframework.security.web.session.ConcurrentSessionFilter;
|
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}.
|
* Tests {@link ConcurrentSessionFilter}.
|
||||||
|
@ -56,9 +55,10 @@ public class ConcurrentSessionFilterTests {
|
||||||
registry.getSessionInformation(session.getId()).expireNow();
|
registry.getSessionInformation(session.getId()).expireNow();
|
||||||
|
|
||||||
// Setup our test fixture and registry to want this session to be expired
|
// Setup our test fixture and registry to want this session to be expired
|
||||||
ConcurrentSessionFilter filter = new ConcurrentSessionFilter(registry,
|
|
||||||
"/expired.jsp");
|
SimpleRedirectExpiredSessionStrategy expiredSessionStrategy = new SimpleRedirectExpiredSessionStrategy("/expired.jsp");
|
||||||
filter.setRedirectStrategy(new DefaultRedirectStrategy());
|
ConcurrentSessionFilter filter = new ConcurrentSessionFilter(registry);
|
||||||
|
filter.setExpiredSessionStrategy(expiredSessionStrategy);
|
||||||
filter.setLogoutHandlers(new LogoutHandler[] { new SecurityContextLogoutHandler() });
|
filter.setLogoutHandlers(new LogoutHandler[] { new SecurityContextLogoutHandler() });
|
||||||
filter.afterPropertiesSet();
|
filter.afterPropertiesSet();
|
||||||
|
|
||||||
|
@ -97,11 +97,6 @@ public class ConcurrentSessionFilterTests {
|
||||||
new ConcurrentSessionFilter(null);
|
new ConcurrentSessionFilter(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
|
||||||
public void detectsInvalidUrl() throws Exception {
|
|
||||||
new ConcurrentSessionFilter(new SessionRegistryImpl(), "ImNotValid");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void lastRequestTimeUpdatesCorrectly() throws Exception {
|
public void lastRequestTimeUpdatesCorrectly() throws Exception {
|
||||||
// Setup our HTTP request
|
// Setup our HTTP request
|
||||||
|
@ -115,11 +110,11 @@ public class ConcurrentSessionFilterTests {
|
||||||
// Setup our test fixture
|
// Setup our test fixture
|
||||||
SessionRegistry registry = new SessionRegistryImpl();
|
SessionRegistry registry = new SessionRegistryImpl();
|
||||||
registry.registerNewSession(session.getId(), "principal");
|
registry.registerNewSession(session.getId(), "principal");
|
||||||
ConcurrentSessionFilter filter = new ConcurrentSessionFilter(registry,
|
SimpleRedirectExpiredSessionStrategy expiredSessionStrategy = new SimpleRedirectExpiredSessionStrategy("/expired.jsp");
|
||||||
"/expired.jsp");
|
ConcurrentSessionFilter filter = new ConcurrentSessionFilter(registry);
|
||||||
|
filter.setExpiredSessionStrategy(expiredSessionStrategy);
|
||||||
|
|
||||||
Date lastRequest = registry.getSessionInformation(session.getId())
|
Date lastRequest = registry.getSessionInformation(session.getId()).getLastRequest();
|
||||||
.getLastRequest();
|
|
||||||
|
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue