mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-09-08 20:51:41 +00:00
SEC-2422: Session timeout detection with CSRF protection
This commit is contained in:
parent
00d668dc5c
commit
7f714ebb23
@ -15,14 +15,22 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.security.config.annotation.web.configurers;
|
package org.springframework.security.config.annotation.web.configurers;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||||
|
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
|
||||||
|
import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
|
||||||
import org.springframework.security.web.csrf.CsrfAuthenticationStrategy;
|
import org.springframework.security.web.csrf.CsrfAuthenticationStrategy;
|
||||||
import org.springframework.security.web.csrf.CsrfFilter;
|
import org.springframework.security.web.csrf.CsrfFilter;
|
||||||
import org.springframework.security.web.csrf.CsrfLogoutHandler;
|
import org.springframework.security.web.csrf.CsrfLogoutHandler;
|
||||||
import org.springframework.security.web.csrf.CsrfTokenRepository;
|
import org.springframework.security.web.csrf.CsrfTokenRepository;
|
||||||
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
|
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
|
||||||
|
import org.springframework.security.web.csrf.MissingCsrfTokenException;
|
||||||
|
import org.springframework.security.web.session.InvalidSessionAccessDeniedHandler;
|
||||||
|
import org.springframework.security.web.session.InvalidSessionStrategy;
|
||||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
@ -50,6 +58,7 @@ import org.springframework.util.Assert;
|
|||||||
* <li>
|
* <li>
|
||||||
* {@link ExceptionHandlingConfigurer#accessDeniedHandler(AccessDeniedHandler)}
|
* {@link ExceptionHandlingConfigurer#accessDeniedHandler(AccessDeniedHandler)}
|
||||||
* is used to determine how to handle CSRF attempts</li>
|
* is used to determine how to handle CSRF attempts</li>
|
||||||
|
* <li>{@link InvalidSessionStrategy}</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
@ -100,12 +109,9 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>> extends Abst
|
|||||||
if(requireCsrfProtectionMatcher != null) {
|
if(requireCsrfProtectionMatcher != null) {
|
||||||
filter.setRequireCsrfProtectionMatcher(requireCsrfProtectionMatcher);
|
filter.setRequireCsrfProtectionMatcher(requireCsrfProtectionMatcher);
|
||||||
}
|
}
|
||||||
ExceptionHandlingConfigurer<H> exceptionConfig = http.getConfigurer(ExceptionHandlingConfigurer.class);
|
AccessDeniedHandler accessDeniedHandler = createAccessDeniedHandler(http);
|
||||||
if(exceptionConfig != null) {
|
if(accessDeniedHandler != null) {
|
||||||
AccessDeniedHandler accessDeniedHandler = exceptionConfig.getAccessDeniedHandler();
|
filter.setAccessDeniedHandler(accessDeniedHandler);
|
||||||
if(accessDeniedHandler != null) {
|
|
||||||
filter.setAccessDeniedHandler(accessDeniedHandler);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);
|
LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);
|
||||||
if(logoutConfigurer != null) {
|
if(logoutConfigurer != null) {
|
||||||
@ -118,4 +124,70 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>> extends Abst
|
|||||||
filter = postProcess(filter);
|
filter = postProcess(filter);
|
||||||
http.addFilter(filter);
|
http.addFilter(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the default {@link AccessDeniedHandler} from the
|
||||||
|
* {@link ExceptionHandlingConfigurer#getAccessDeniedHandler()} or create a
|
||||||
|
* {@link AccessDeniedHandlerImpl} if not available.
|
||||||
|
*
|
||||||
|
* @param http the {@link HttpSecurityBuilder}
|
||||||
|
* @return the {@link AccessDeniedHandler}
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private AccessDeniedHandler getDefaultAccessDeniedHandler(H http) {
|
||||||
|
ExceptionHandlingConfigurer<H> exceptionConfig = http.getConfigurer(ExceptionHandlingConfigurer.class);
|
||||||
|
AccessDeniedHandler handler = null;
|
||||||
|
if(exceptionConfig != null) {
|
||||||
|
handler = exceptionConfig.getAccessDeniedHandler();
|
||||||
|
}
|
||||||
|
if(handler == null) {
|
||||||
|
handler = new AccessDeniedHandlerImpl();
|
||||||
|
}
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the default {@link InvalidSessionStrategy} from the
|
||||||
|
* {@link SessionManagementConfigurer#getInvalidSessionStrategy()} or null
|
||||||
|
* if not available.
|
||||||
|
*
|
||||||
|
* @param http
|
||||||
|
* the {@link HttpSecurityBuilder}
|
||||||
|
* @return the {@link InvalidSessionStrategy}
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private InvalidSessionStrategy getInvalidSessionStrategy(H http) {
|
||||||
|
SessionManagementConfigurer<H> sessionManagement = http.getConfigurer(SessionManagementConfigurer.class);
|
||||||
|
if(sessionManagement == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return sessionManagement.getInvalidSessionStrategy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the {@link AccessDeniedHandler} from the result of
|
||||||
|
* {@link #getDefaultAccessDeniedHandler(HttpSecurityBuilder)} and
|
||||||
|
* {@link #getInvalidSessionStrategy(HttpSecurityBuilder)}. If
|
||||||
|
* {@link #getInvalidSessionStrategy(HttpSecurityBuilder)} is non-null, then
|
||||||
|
* a {@link DelegatingAccessDeniedHandler} is used in combination with
|
||||||
|
* {@link InvalidSessionAccessDeniedHandler} and the
|
||||||
|
* {@link #getDefaultAccessDeniedHandler(HttpSecurityBuilder)}. Otherwise,
|
||||||
|
* only {@link #getDefaultAccessDeniedHandler(HttpSecurityBuilder)} is used.
|
||||||
|
*
|
||||||
|
* @param http the {@link HttpSecurityBuilder}
|
||||||
|
* @return the {@link AccessDeniedHandler}
|
||||||
|
*/
|
||||||
|
private AccessDeniedHandler createAccessDeniedHandler(H http) {
|
||||||
|
InvalidSessionStrategy invalidSessionStrategy = getInvalidSessionStrategy(http);
|
||||||
|
AccessDeniedHandler defaultAccessDeniedHandler = getDefaultAccessDeniedHandler(http);
|
||||||
|
if(invalidSessionStrategy == null) {
|
||||||
|
return defaultAccessDeniedHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
InvalidSessionAccessDeniedHandler invalidSessionDeniedHandler = new InvalidSessionAccessDeniedHandler(invalidSessionStrategy);
|
||||||
|
LinkedHashMap<Class<? extends AccessDeniedException>, AccessDeniedHandler> handlers =
|
||||||
|
new LinkedHashMap<Class<? extends AccessDeniedException>, AccessDeniedHandler>();
|
||||||
|
handlers.put(MissingCsrfTokenException.class, invalidSessionDeniedHandler);
|
||||||
|
return new DelegatingAccessDeniedHandler(handlers, defaultAccessDeniedHandler);
|
||||||
|
}
|
||||||
}
|
}
|
@ -42,6 +42,7 @@ 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.InvalidSessionStrategy;
|
||||||
import org.springframework.security.web.session.SessionManagementFilter;
|
import org.springframework.security.web.session.SessionManagementFilter;
|
||||||
import org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy;
|
import org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
@ -66,6 +67,7 @@ import org.springframework.util.Assert;
|
|||||||
* <li>{@link RequestCache}</li>
|
* <li>{@link RequestCache}</li>
|
||||||
* <li>{@link SecurityContextRepository}</li>
|
* <li>{@link SecurityContextRepository}</li>
|
||||||
* <li>{@link SessionManagementConfigurer}</li>
|
* <li>{@link SessionManagementConfigurer}</li>
|
||||||
|
* <li>{@link InvalidSessionStrategy}</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <h2>Shared Objects Used</h2>
|
* <h2>Shared Objects Used</h2>
|
||||||
@ -83,6 +85,7 @@ import org.springframework.util.Assert;
|
|||||||
public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<SessionManagementConfigurer<H>,H> {
|
public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<SessionManagementConfigurer<H>,H> {
|
||||||
private SessionAuthenticationStrategy sessionFixationAuthenticationStrategy = createDefaultSessionFixationProtectionStrategy();
|
private SessionAuthenticationStrategy sessionFixationAuthenticationStrategy = createDefaultSessionFixationProtectionStrategy();
|
||||||
private SessionAuthenticationStrategy sessionAuthenticationStrategy;
|
private SessionAuthenticationStrategy sessionAuthenticationStrategy;
|
||||||
|
private InvalidSessionStrategy invalidSessionStrategy;
|
||||||
private List<SessionAuthenticationStrategy> sessionAuthenticationStrategies = new ArrayList<SessionAuthenticationStrategy>();
|
private List<SessionAuthenticationStrategy> sessionAuthenticationStrategies = new ArrayList<SessionAuthenticationStrategy>();
|
||||||
private SessionRegistry sessionRegistry = new SessionRegistryImpl();
|
private SessionRegistry sessionRegistry = new SessionRegistryImpl();
|
||||||
private Integer maximumSessions;
|
private Integer maximumSessions;
|
||||||
@ -365,6 +368,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
http.setSharedObject(SessionAuthenticationStrategy.class, getSessionAuthenticationStrategy());
|
http.setSharedObject(SessionAuthenticationStrategy.class, getSessionAuthenticationStrategy());
|
||||||
|
http.setSharedObject(InvalidSessionStrategy.class, getInvalidSessionStrategy());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -375,7 +379,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
|||||||
sessionManagementFilter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler(sessionAuthenticationErrorUrl));
|
sessionManagementFilter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler(sessionAuthenticationErrorUrl));
|
||||||
}
|
}
|
||||||
if(invalidSessionUrl != null) {
|
if(invalidSessionUrl != null) {
|
||||||
sessionManagementFilter.setInvalidSessionStrategy(new SimpleRedirectInvalidSessionStrategy(invalidSessionUrl));
|
sessionManagementFilter.setInvalidSessionStrategy(getInvalidSessionStrategy());
|
||||||
}
|
}
|
||||||
AuthenticationTrustResolver trustResolver = http.getSharedObject(AuthenticationTrustResolver.class);
|
AuthenticationTrustResolver trustResolver = http.getSharedObject(AuthenticationTrustResolver.class);
|
||||||
if(trustResolver != null) {
|
if(trustResolver != null) {
|
||||||
@ -391,6 +395,23 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link InvalidSessionStrategy} to use. If
|
||||||
|
* {@link #invalidSessionUrl} is null, returns null otherwise
|
||||||
|
* {@link SimpleRedirectInvalidSessionStrategy} is used.
|
||||||
|
*
|
||||||
|
* @return the {@link InvalidSessionStrategy} to use
|
||||||
|
*/
|
||||||
|
InvalidSessionStrategy getInvalidSessionStrategy() {
|
||||||
|
if(invalidSessionUrl == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if(invalidSessionStrategy == null) {
|
||||||
|
invalidSessionStrategy = new SimpleRedirectInvalidSessionStrategy(invalidSessionUrl);
|
||||||
|
}
|
||||||
|
return invalidSessionStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the {@link SessionCreationPolicy}. Can not be null.
|
* Gets the {@link SessionCreationPolicy}. Can not be null.
|
||||||
* @return the {@link SessionCreationPolicy}
|
* @return the {@link SessionCreationPolicy}
|
||||||
|
@ -15,17 +15,26 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.security.config.http;
|
package org.springframework.security.config.http;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeanMetadataElement;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||||
|
import org.springframework.beans.factory.support.ManagedMap;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||||
import org.springframework.beans.factory.xml.ParserContext;
|
import org.springframework.beans.factory.xml.ParserContext;
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||||
|
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||||
|
import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
|
||||||
import org.springframework.security.web.csrf.CsrfAuthenticationStrategy;
|
import org.springframework.security.web.csrf.CsrfAuthenticationStrategy;
|
||||||
import org.springframework.security.web.csrf.CsrfFilter;
|
import org.springframework.security.web.csrf.CsrfFilter;
|
||||||
import org.springframework.security.web.csrf.CsrfLogoutHandler;
|
import org.springframework.security.web.csrf.CsrfLogoutHandler;
|
||||||
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
|
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
|
||||||
|
import org.springframework.security.web.csrf.MissingCsrfTokenException;
|
||||||
import org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor;
|
import org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor;
|
||||||
|
import org.springframework.security.web.session.InvalidSessionAccessDeniedHandler;
|
||||||
|
import org.springframework.security.web.session.InvalidSessionStrategy;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
@ -44,6 +53,7 @@ public class CsrfBeanDefinitionParser implements BeanDefinitionParser {
|
|||||||
private static final String ATT_REPOSITORY = "token-repository-ref";
|
private static final String ATT_REPOSITORY = "token-repository-ref";
|
||||||
|
|
||||||
private String csrfRepositoryRef;
|
private String csrfRepositoryRef;
|
||||||
|
private BeanDefinition csrfFilter;
|
||||||
|
|
||||||
public BeanDefinition parse(Element element, ParserContext pc) {
|
public BeanDefinition parse(Element element, ParserContext pc) {
|
||||||
boolean webmvcPresent = ClassUtils.isPresent(DISPATCHER_SERVLET_CLASS_NAME, getClass().getClassLoader());
|
boolean webmvcPresent = ClassUtils.isPresent(DISPATCHER_SERVLET_CLASS_NAME, getClass().getClassLoader());
|
||||||
@ -58,21 +68,64 @@ public class CsrfBeanDefinitionParser implements BeanDefinitionParser {
|
|||||||
csrfRepositoryRef = element.getAttribute(ATT_REPOSITORY);
|
csrfRepositoryRef = element.getAttribute(ATT_REPOSITORY);
|
||||||
String matcherRef = element.getAttribute(ATT_MATCHER);
|
String matcherRef = element.getAttribute(ATT_MATCHER);
|
||||||
|
|
||||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(CsrfFilter.class);
|
|
||||||
|
|
||||||
if(!StringUtils.hasText(csrfRepositoryRef)) {
|
if(!StringUtils.hasText(csrfRepositoryRef)) {
|
||||||
RootBeanDefinition csrfTokenRepository = new RootBeanDefinition(HttpSessionCsrfTokenRepository.class);
|
RootBeanDefinition csrfTokenRepository = new RootBeanDefinition(HttpSessionCsrfTokenRepository.class);
|
||||||
csrfRepositoryRef = pc.getReaderContext().generateBeanName(csrfTokenRepository);
|
csrfRepositoryRef = pc.getReaderContext().generateBeanName(csrfTokenRepository);
|
||||||
pc.registerBeanComponent(new BeanComponentDefinition(csrfTokenRepository, csrfRepositoryRef));
|
pc.registerBeanComponent(new BeanComponentDefinition(csrfTokenRepository, csrfRepositoryRef));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(CsrfFilter.class);
|
||||||
builder.addConstructorArgReference(csrfRepositoryRef);
|
builder.addConstructorArgReference(csrfRepositoryRef);
|
||||||
|
|
||||||
if(StringUtils.hasText(matcherRef)) {
|
if(StringUtils.hasText(matcherRef)) {
|
||||||
builder.addPropertyReference("requireCsrfProtectionMatcher", matcherRef);
|
builder.addPropertyReference("requireCsrfProtectionMatcher", matcherRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.getBeanDefinition();
|
csrfFilter = builder.getBeanDefinition();
|
||||||
|
return csrfFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populate the AccessDeniedHandler on the {@link CsrfFilter}
|
||||||
|
*
|
||||||
|
* @param invalidSessionStrategy the {@link InvalidSessionStrategy} to use
|
||||||
|
* @param defaultDeniedHandler the {@link AccessDeniedHandler} to use
|
||||||
|
*/
|
||||||
|
void initAccessDeniedHandler(BeanDefinition invalidSessionStrategy, BeanMetadataElement defaultDeniedHandler) {
|
||||||
|
BeanMetadataElement accessDeniedHandler = createAccessDeniedHandler(invalidSessionStrategy, defaultDeniedHandler);
|
||||||
|
csrfFilter.getPropertyValues().addPropertyValue("accessDeniedHandler", accessDeniedHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the {@link AccessDeniedHandler} from the result of
|
||||||
|
* {@link #getDefaultAccessDeniedHandler(HttpSecurityBuilder)} and
|
||||||
|
* {@link #getInvalidSessionStrategy(HttpSecurityBuilder)}. If
|
||||||
|
* {@link #getInvalidSessionStrategy(HttpSecurityBuilder)} is non-null, then
|
||||||
|
* a {@link DelegatingAccessDeniedHandler} is used in combination with
|
||||||
|
* {@link InvalidSessionAccessDeniedHandler} and the
|
||||||
|
* {@link #getDefaultAccessDeniedHandler(HttpSecurityBuilder)}. Otherwise,
|
||||||
|
* only {@link #getDefaultAccessDeniedHandler(HttpSecurityBuilder)} is used.
|
||||||
|
*
|
||||||
|
* @param invalidSessionStrategy the {@link InvalidSessionStrategy} to use
|
||||||
|
* @param defaultDeniedHandler the {@link AccessDeniedHandler} to use
|
||||||
|
*
|
||||||
|
* @return the {@link BeanMetadataElement} that is the {@link AccessDeniedHandler} to populate on the {@link CsrfFilter}
|
||||||
|
*/
|
||||||
|
private BeanMetadataElement createAccessDeniedHandler(BeanDefinition invalidSessionStrategy, BeanMetadataElement defaultDeniedHandler) {
|
||||||
|
if(invalidSessionStrategy == null) {
|
||||||
|
return defaultDeniedHandler;
|
||||||
|
}
|
||||||
|
ManagedMap<Class<? extends AccessDeniedException>,BeanDefinition> handlers =
|
||||||
|
new ManagedMap<Class<? extends AccessDeniedException>, BeanDefinition>();
|
||||||
|
BeanDefinitionBuilder invalidSessionHandlerBldr = BeanDefinitionBuilder.rootBeanDefinition(InvalidSessionAccessDeniedHandler.class);
|
||||||
|
invalidSessionHandlerBldr.addConstructorArgValue(invalidSessionStrategy);
|
||||||
|
handlers.put(MissingCsrfTokenException.class, invalidSessionHandlerBldr.getBeanDefinition());
|
||||||
|
|
||||||
|
BeanDefinitionBuilder deniedBldr = BeanDefinitionBuilder.rootBeanDefinition(DelegatingAccessDeniedHandler.class);
|
||||||
|
deniedBldr.addConstructorArgValue(handlers);
|
||||||
|
deniedBldr.addConstructorArgValue(defaultDeniedHandler);
|
||||||
|
|
||||||
|
return deniedBldr.getBeanDefinition();
|
||||||
}
|
}
|
||||||
|
|
||||||
BeanDefinition getCsrfAuthenticationStrategy() {
|
BeanDefinition getCsrfAuthenticationStrategy() {
|
||||||
|
@ -130,6 +130,10 @@ class HttpConfigurationBuilder {
|
|||||||
private BeanMetadataElement csrfLogoutHandler;
|
private BeanMetadataElement csrfLogoutHandler;
|
||||||
private BeanMetadataElement csrfAuthStrategy;
|
private BeanMetadataElement csrfAuthStrategy;
|
||||||
|
|
||||||
|
private CsrfBeanDefinitionParser csrfParser;
|
||||||
|
|
||||||
|
private BeanDefinition invalidSession;
|
||||||
|
|
||||||
public HttpConfigurationBuilder(Element element, ParserContext pc,
|
public HttpConfigurationBuilder(Element element, ParserContext pc,
|
||||||
BeanReference portMapper, BeanReference portResolver, BeanReference authenticationManager) {
|
BeanReference portMapper, BeanReference portResolver, BeanReference authenticationManager) {
|
||||||
this.httpElt = element;
|
this.httpElt = element;
|
||||||
@ -200,8 +204,8 @@ class HttpConfigurationBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setAccessDeniedHandler(BeanMetadataElement accessDeniedHandler) {
|
void setAccessDeniedHandler(BeanMetadataElement accessDeniedHandler) {
|
||||||
if(csrfFilter != null) {
|
if(csrfParser != null ) {
|
||||||
csrfFilter.getPropertyValues().add("accessDeniedHandler", accessDeniedHandler);
|
csrfParser.initAccessDeniedHandler(this.invalidSession, accessDeniedHandler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -381,7 +385,10 @@ class HttpConfigurationBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtils.hasText(invalidSessionUrl)) {
|
if (StringUtils.hasText(invalidSessionUrl)) {
|
||||||
sessionMgmtFilter.addPropertyValue("invalidSessionStrategy", new SimpleRedirectInvalidSessionStrategy(invalidSessionUrl));
|
BeanDefinitionBuilder invalidSessionBldr = BeanDefinitionBuilder.rootBeanDefinition(SimpleRedirectInvalidSessionStrategy.class);
|
||||||
|
invalidSessionBldr.addConstructorArgValue(invalidSessionUrl);
|
||||||
|
invalidSession = invalidSessionBldr.getBeanDefinition();
|
||||||
|
sessionMgmtFilter.addPropertyValue("invalidSessionStrategy", invalidSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionMgmtFilter.addConstructorArgReference(sessionAuthStratRef);
|
sessionMgmtFilter.addConstructorArgReference(sessionAuthStratRef);
|
||||||
@ -637,14 +644,16 @@ class HttpConfigurationBuilder {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createCsrfFilter() {
|
private CsrfBeanDefinitionParser createCsrfFilter() {
|
||||||
Element elmt = DomUtils.getChildElementByTagName(httpElt, Elements.CSRF);
|
Element elmt = DomUtils.getChildElementByTagName(httpElt, Elements.CSRF);
|
||||||
if (elmt != null) {
|
if (elmt != null) {
|
||||||
CsrfBeanDefinitionParser csrfParser = new CsrfBeanDefinitionParser();
|
csrfParser = new CsrfBeanDefinitionParser();
|
||||||
this.csrfFilter = csrfParser.parse(elmt, pc);
|
csrfFilter = csrfParser.parse(elmt, pc);
|
||||||
this.csrfAuthStrategy = csrfParser.getCsrfAuthenticationStrategy();
|
this.csrfAuthStrategy = csrfParser.getCsrfAuthenticationStrategy();
|
||||||
this.csrfLogoutHandler = csrfParser.getCsrfLogoutHandler();
|
this.csrfLogoutHandler = csrfParser.getCsrfLogoutHandler();
|
||||||
|
return csrfParser;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
BeanMetadataElement getCsrfLogoutHandler() {
|
BeanMetadataElement getCsrfLogoutHandler() {
|
||||||
|
@ -18,19 +18,20 @@ package org.springframework.security.config.annotation.web.configurers
|
|||||||
import javax.servlet.http.HttpServletResponse
|
import javax.servlet.http.HttpServletResponse
|
||||||
|
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.mock.web.MockHttpServletRequest
|
||||||
|
import org.springframework.mock.web.MockHttpServletResponse
|
||||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
|
||||||
import org.springframework.security.web.access.AccessDeniedHandler
|
import org.springframework.security.web.access.AccessDeniedHandler
|
||||||
import org.springframework.security.web.csrf.CsrfFilter;
|
import org.springframework.security.web.csrf.CsrfFilter
|
||||||
import org.springframework.security.web.csrf.CsrfTokenRepository;
|
import org.springframework.security.web.csrf.CsrfTokenRepository
|
||||||
import org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor;
|
import org.springframework.security.web.util.matcher.RequestMatcher
|
||||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
import org.springframework.web.servlet.support.RequestDataValueProcessor
|
||||||
import org.springframework.web.servlet.support.RequestDataValueProcessor;
|
|
||||||
|
|
||||||
import spock.lang.Unroll;
|
import spock.lang.Unroll
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -100,6 +101,37 @@ class CsrfConfigurerTests extends BaseSpringSpec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def "SEC-2422: csrf expire CSRF token and session-management invalid-session-url"() {
|
||||||
|
setup:
|
||||||
|
loadConfig(InvalidSessionUrlConfig)
|
||||||
|
request.session.clearAttributes()
|
||||||
|
request.setParameter("_csrf","abc")
|
||||||
|
request.method = "POST"
|
||||||
|
when: "No existing expected CsrfToken (session times out) and a POST"
|
||||||
|
springSecurityFilterChain.doFilter(request,response,chain)
|
||||||
|
then: "sent to the session timeout page page"
|
||||||
|
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
|
||||||
|
response.redirectedUrl == "/error/sessionError"
|
||||||
|
when: "Existing expected CsrfToken and a POST (invalid token provided)"
|
||||||
|
response = new MockHttpServletResponse()
|
||||||
|
request = new MockHttpServletRequest(session: request.session, method:'POST')
|
||||||
|
springSecurityFilterChain.doFilter(request,response,chain)
|
||||||
|
then: "Access Denied occurs"
|
||||||
|
response.status == HttpServletResponse.SC_FORBIDDEN
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class InvalidSessionUrlConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
http
|
||||||
|
.csrf().and()
|
||||||
|
.sessionManagement()
|
||||||
|
.invalidSessionUrl("/error/sessionError")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def "csrf requireCsrfProtectionMatcher"() {
|
def "csrf requireCsrfProtectionMatcher"() {
|
||||||
setup:
|
setup:
|
||||||
RequireCsrfProtectionMatcherConfig.matcher = Mock(RequestMatcher)
|
RequireCsrfProtectionMatcherConfig.matcher = Mock(RequestMatcher)
|
||||||
|
@ -174,6 +174,28 @@ class CsrfConfigTests extends AbstractHttpConfigTests {
|
|||||||
response.redirectedUrl == "http://localhost/some-url"
|
response.redirectedUrl == "http://localhost/some-url"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def "SEC-2422: csrf expire CSRF token and session-management invalid-session-url"() {
|
||||||
|
setup:
|
||||||
|
httpAutoConfig {
|
||||||
|
'csrf'()
|
||||||
|
'session-management'('invalid-session-url': '/error/sessionError')
|
||||||
|
}
|
||||||
|
createAppContext()
|
||||||
|
request.setParameter("_csrf","abc")
|
||||||
|
request.method = "POST"
|
||||||
|
when: "No existing expected CsrfToken (session times out) and a POST"
|
||||||
|
springSecurityFilterChain.doFilter(request,response,chain)
|
||||||
|
then: "sent to the session timeout page page"
|
||||||
|
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
|
||||||
|
response.redirectedUrl == "/error/sessionError"
|
||||||
|
when: "Existing expected CsrfToken and a POST (invalid token provided)"
|
||||||
|
response = new MockHttpServletResponse()
|
||||||
|
request = new MockHttpServletRequest(session: request.session, method:'POST')
|
||||||
|
springSecurityFilterChain.doFilter(request,response,chain)
|
||||||
|
then: "Access Denied occurs"
|
||||||
|
response.status == HttpServletResponse.SC_FORBIDDEN
|
||||||
|
}
|
||||||
|
|
||||||
def "csrf requireCsrfProtectionMatcher"() {
|
def "csrf requireCsrfProtectionMatcher"() {
|
||||||
setup:
|
setup:
|
||||||
httpAutoConfig {
|
httpAutoConfig {
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2013 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.access;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link AccessDeniedHandler} that delegates to other
|
||||||
|
* {@link AccessDeniedHandler} instances based upon the type of
|
||||||
|
* {@link AccessDeniedException} passed into
|
||||||
|
* {@link #handle(HttpServletRequest, HttpServletResponse, AccessDeniedException)}.
|
||||||
|
*
|
||||||
|
* @author Rob Winch
|
||||||
|
* @since 3.2
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class DelegatingAccessDeniedHandler implements AccessDeniedHandler {
|
||||||
|
private final LinkedHashMap<Class<? extends AccessDeniedException>, AccessDeniedHandler> handlers;
|
||||||
|
|
||||||
|
private final AccessDeniedHandler defaultHander;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param handlers
|
||||||
|
* a map of the {@link AccessDeniedException} class to the
|
||||||
|
* {@link AccessDeniedHandler} that should be used. Each is
|
||||||
|
* considered in the order they are specified and only the first
|
||||||
|
* {@link AccessDeniedHandler} is ued.
|
||||||
|
* @param defaultHander
|
||||||
|
* the default {@link AccessDeniedHandler} that should be used if
|
||||||
|
* none of the handlers matches.
|
||||||
|
*/
|
||||||
|
public DelegatingAccessDeniedHandler(
|
||||||
|
LinkedHashMap<Class<? extends AccessDeniedException>, AccessDeniedHandler> handlers,
|
||||||
|
AccessDeniedHandler defaultHander) {
|
||||||
|
Assert.notEmpty(handlers, "handlers cannot be null or empty");
|
||||||
|
Assert.notNull(defaultHander, "defaultHandler cannot be null");
|
||||||
|
this.handlers = handlers;
|
||||||
|
this.defaultHander = defaultHander;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void handle(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
AccessDeniedException accessDeniedException) throws IOException,
|
||||||
|
ServletException {
|
||||||
|
for(Entry<Class<? extends AccessDeniedException>, AccessDeniedHandler> entry : handlers.entrySet()) {
|
||||||
|
Class<? extends AccessDeniedException> handlerClass = entry.getKey();
|
||||||
|
if(handlerClass.isAssignableFrom(accessDeniedException.getClass())) {
|
||||||
|
AccessDeniedHandler handler = entry.getValue();
|
||||||
|
handler.handle(request, response, accessDeniedException);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defaultHander.handle(request, response, accessDeniedException);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2013 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.csrf;
|
||||||
|
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when an invalid or missing {@link CsrfToken} is found in the HttpServletRequest
|
||||||
|
*
|
||||||
|
* @author Rob Winch
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public class CsrfException extends AccessDeniedException {
|
||||||
|
|
||||||
|
public CsrfException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -73,7 +73,8 @@ public final class CsrfFilter extends OncePerRequestFilter {
|
|||||||
HttpServletResponse response, FilterChain filterChain)
|
HttpServletResponse response, FilterChain filterChain)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
CsrfToken csrfToken = tokenRepository.loadToken(request);
|
CsrfToken csrfToken = tokenRepository.loadToken(request);
|
||||||
if(csrfToken == null) {
|
final boolean missingToken = csrfToken == null;
|
||||||
|
if(missingToken) {
|
||||||
CsrfToken generatedToken = tokenRepository.generateToken(request);
|
CsrfToken generatedToken = tokenRepository.generateToken(request);
|
||||||
csrfToken = new SaveOnAccessCsrfToken(tokenRepository, request, response, generatedToken);
|
csrfToken = new SaveOnAccessCsrfToken(tokenRepository, request, response, generatedToken);
|
||||||
}
|
}
|
||||||
@ -93,7 +94,11 @@ public final class CsrfFilter extends OncePerRequestFilter {
|
|||||||
if(logger.isDebugEnabled()) {
|
if(logger.isDebugEnabled()) {
|
||||||
logger.debug("Invalid CSRF token found for " + UrlUtils.buildFullRequestUrl(request));
|
logger.debug("Invalid CSRF token found for " + UrlUtils.buildFullRequestUrl(request));
|
||||||
}
|
}
|
||||||
accessDeniedHandler.handle(request, response, new InvalidCsrfTokenException(csrfToken, actualToken));
|
if(missingToken) {
|
||||||
|
accessDeniedHandler.handle(request, response, new MissingCsrfTokenException(actualToken));
|
||||||
|
} else {
|
||||||
|
accessDeniedHandler.handle(request, response, new InvalidCsrfTokenException(csrfToken, actualToken));
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,17 +15,17 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.security.web.csrf;
|
package org.springframework.security.web.csrf;
|
||||||
|
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown when an invalid or missing {@link CsrfToken} is found in the HttpServletRequest
|
* Thrown when an expected {@link CsrfToken} exists, but it does not match the
|
||||||
|
* value present on the {@link HttpServletRequest}
|
||||||
*
|
*
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
* @since 3.2
|
* @since 3.2
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class InvalidCsrfTokenException extends AccessDeniedException {
|
public class InvalidCsrfTokenException extends CsrfException {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param msg
|
* @param msg
|
||||||
@ -36,5 +36,4 @@ public class InvalidCsrfTokenException extends AccessDeniedException {
|
|||||||
+ expectedAccessToken.getParameterName() + "' or header '"
|
+ expectedAccessToken.getParameterName() + "' or header '"
|
||||||
+ expectedAccessToken.getHeaderName() + "'.");
|
+ expectedAccessToken.getHeaderName() + "'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2013 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.csrf;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when no expected {@link CsrfToken} is found but is required.
|
||||||
|
*
|
||||||
|
* @author Rob Winch
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public class MissingCsrfTokenException extends CsrfException {
|
||||||
|
|
||||||
|
public MissingCsrfTokenException(String actualToken) {
|
||||||
|
super("Expected CSRF token not found. Has your session expired?");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2013 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;
|
||||||
|
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An adapter of {@link InvalidSessionStrategy} to {@link AccessDeniedHandler}
|
||||||
|
*
|
||||||
|
* @author Rob Winch
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
public final class InvalidSessionAccessDeniedHandler implements AccessDeniedHandler {
|
||||||
|
private final InvalidSessionStrategy invalidSessionStrategy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
* @param invalidSessionStrategy the {@link InvalidSessionStrategy} to delegate to
|
||||||
|
*/
|
||||||
|
public InvalidSessionAccessDeniedHandler(
|
||||||
|
InvalidSessionStrategy invalidSessionStrategy) {
|
||||||
|
Assert.notNull(invalidSessionStrategy, "invalidSessionStrategy cannot be null");
|
||||||
|
this.invalidSessionStrategy = invalidSessionStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
AccessDeniedException accessDeniedException) throws IOException,
|
||||||
|
ServletException {
|
||||||
|
invalidSessionStrategy.onInvalidSessionDetected(request, response);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2013 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.access;
|
||||||
|
|
||||||
|
import static org.mockito.Matchers.any;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
import org.springframework.security.web.csrf.CsrfException;
|
||||||
|
import org.springframework.security.web.csrf.InvalidCsrfTokenException;
|
||||||
|
import org.springframework.security.web.csrf.MissingCsrfTokenException;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class DelegatingAccessDeniedHandlerTests {
|
||||||
|
@Mock
|
||||||
|
private AccessDeniedHandler handler1;
|
||||||
|
@Mock
|
||||||
|
private AccessDeniedHandler handler2;
|
||||||
|
@Mock
|
||||||
|
private AccessDeniedHandler handler3;
|
||||||
|
@Mock
|
||||||
|
private HttpServletRequest request;
|
||||||
|
@Mock
|
||||||
|
private HttpServletResponse response;
|
||||||
|
|
||||||
|
private LinkedHashMap<Class<? extends AccessDeniedException>,AccessDeniedHandler> handlers;
|
||||||
|
|
||||||
|
private DelegatingAccessDeniedHandler handler;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
handlers = new LinkedHashMap<Class<? extends AccessDeniedException>, AccessDeniedHandler>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void moreSpecificDoesNotInvokeLessSpecific() throws Exception {
|
||||||
|
handlers.put(CsrfException.class, handler1);
|
||||||
|
handler = new DelegatingAccessDeniedHandler(handlers, handler3);
|
||||||
|
|
||||||
|
AccessDeniedException accessDeniedException = new AccessDeniedException("");
|
||||||
|
handler.handle(request, response, accessDeniedException);
|
||||||
|
|
||||||
|
verify(handler1,never()).handle(any(HttpServletRequest.class), any(HttpServletResponse.class), any(AccessDeniedException.class));
|
||||||
|
verify(handler3).handle(request, response, accessDeniedException);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void matchesDoesNotInvokeDefault() throws Exception {
|
||||||
|
handlers.put(InvalidCsrfTokenException.class, handler1);
|
||||||
|
handlers.put(MissingCsrfTokenException.class, handler2);
|
||||||
|
handler = new DelegatingAccessDeniedHandler(handlers, handler3);
|
||||||
|
|
||||||
|
AccessDeniedException accessDeniedException = new MissingCsrfTokenException("123");
|
||||||
|
handler.handle(request, response, accessDeniedException);
|
||||||
|
|
||||||
|
verify(handler1,never()).handle(any(HttpServletRequest.class), any(HttpServletResponse.class), any(AccessDeniedException.class));
|
||||||
|
verify(handler2).handle(request, response, accessDeniedException);
|
||||||
|
verify(handler3,never()).handle(any(HttpServletRequest.class), any(HttpServletResponse.class), any(AccessDeniedException.class));
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user