Add Csrf Ignore Configurability

Issue gh-5185
This commit is contained in:
Josh Cummings 2019-12-30 09:10:49 -07:00
parent 0ba3ff6df2
commit 98a2ca3bbc
4 changed files with 89 additions and 20 deletions

View File

@ -15,8 +15,18 @@
*/ */
package org.springframework.security.config.http; package org.springframework.security.config.http;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Element;
import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference; import org.springframework.beans.factory.config.BeanReference;
@ -53,15 +63,6 @@ import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils; import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
import javax.servlet.http.HttpServletRequest;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import static org.springframework.security.config.http.SecurityFilters.ANONYMOUS_FILTER; import static org.springframework.security.config.http.SecurityFilters.ANONYMOUS_FILTER;
import static org.springframework.security.config.http.SecurityFilters.BASIC_AUTH_FILTER; import static org.springframework.security.config.http.SecurityFilters.BASIC_AUTH_FILTER;
@ -160,11 +161,12 @@ final class AuthenticationConfigBuilder {
private BeanReference oauth2LoginAuthenticationProviderRef; private BeanReference oauth2LoginAuthenticationProviderRef;
private BeanReference oauth2LoginOidcAuthenticationProviderRef; private BeanReference oauth2LoginOidcAuthenticationProviderRef;
private BeanDefinition oauth2LoginLinks; private BeanDefinition oauth2LoginLinks;
private BeanDefinition authorizationRequestRedirectFilter; private BeanDefinition authorizationRequestRedirectFilter;
private BeanDefinition authorizationCodeGrantFilter; private BeanDefinition authorizationCodeGrantFilter;
private BeanReference authorizationCodeAuthenticationProviderRef; private BeanReference authorizationCodeAuthenticationProviderRef;
private final List<BeanDefinition> csrfIgnoreRequestMatchers = new ManagedList<>();
AuthenticationConfigBuilder(Element element, boolean forceAutoConfig, AuthenticationConfigBuilder(Element element, boolean forceAutoConfig,
ParserContext pc, SessionCreationPolicy sessionPolicy, ParserContext pc, SessionCreationPolicy sessionPolicy,
BeanReference requestCache, BeanReference authenticationManager, BeanReference requestCache, BeanReference authenticationManager,
@ -194,7 +196,6 @@ final class AuthenticationConfigBuilder {
createLoginPageFilterIfNeeded(); createLoginPageFilterIfNeeded();
createUserDetailsServiceFactory(); createUserDetailsServiceFactory();
createExceptionTranslationFilter(); createExceptionTranslationFilter();
} }
void createRememberMeFilter(BeanReference authenticationManager) { void createRememberMeFilter(BeanReference authenticationManager) {
@ -708,6 +709,10 @@ final class AuthenticationConfigBuilder {
return accessDeniedHandler; return accessDeniedHandler;
} }
List<BeanDefinition> getCsrfIgnoreRequestMatchers() {
return csrfIgnoreRequestMatchers;
}
void createAnonymousFilter() { void createAnonymousFilter() {
Element anonymousElt = DomUtils.getChildElementByTagName(httpElt, Element anonymousElt = DomUtils.getChildElementByTagName(httpElt,
Elements.ANONYMOUS); Elements.ANONYMOUS);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2020 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.
@ -15,12 +15,19 @@
*/ */
package org.springframework.security.config.http; package org.springframework.security.config.http;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
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.ManagedList;
import org.springframework.beans.factory.support.ManagedMap; 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;
@ -38,6 +45,10 @@ 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.InvalidSessionAccessDeniedHandler;
import org.springframework.security.web.session.InvalidSessionStrategy; import org.springframework.security.web.session.InvalidSessionStrategy;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -58,6 +69,8 @@ public class CsrfBeanDefinitionParser implements BeanDefinitionParser {
private String csrfRepositoryRef; private String csrfRepositoryRef;
private BeanDefinition csrfFilter; private BeanDefinition csrfFilter;
private String requestMatcherRef;
@Override @Override
public BeanDefinition parse(Element element, ParserContext pc) { public BeanDefinition parse(Element element, ParserContext pc) {
boolean disabled = element != null boolean disabled = element != null
@ -77,10 +90,9 @@ public class CsrfBeanDefinitionParser implements BeanDefinitionParser {
} }
} }
String matcherRef = null;
if (element != null) { if (element != null) {
this.csrfRepositoryRef = element.getAttribute(ATT_REPOSITORY); this.csrfRepositoryRef = element.getAttribute(ATT_REPOSITORY);
matcherRef = element.getAttribute(ATT_MATCHER); this.requestMatcherRef = element.getAttribute(ATT_MATCHER);
} }
if (!StringUtils.hasText(this.csrfRepositoryRef)) { if (!StringUtils.hasText(this.csrfRepositoryRef)) {
@ -100,8 +112,8 @@ public class CsrfBeanDefinitionParser implements BeanDefinitionParser {
.rootBeanDefinition(CsrfFilter.class); .rootBeanDefinition(CsrfFilter.class);
builder.addConstructorArgReference(this.csrfRepositoryRef); builder.addConstructorArgReference(this.csrfRepositoryRef);
if (StringUtils.hasText(matcherRef)) { if (StringUtils.hasText(this.requestMatcherRef)) {
builder.addPropertyReference("requireCsrfProtectionMatcher", matcherRef); builder.addPropertyReference("requireCsrfProtectionMatcher", this.requestMatcherRef);
} }
this.csrfFilter = builder.getBeanDefinition(); this.csrfFilter = builder.getBeanDefinition();
@ -172,4 +184,46 @@ public class CsrfBeanDefinitionParser implements BeanDefinitionParser {
csrfAuthenticationStrategy.addConstructorArgReference(this.csrfRepositoryRef); csrfAuthenticationStrategy.addConstructorArgReference(this.csrfRepositoryRef);
return csrfAuthenticationStrategy.getBeanDefinition(); return csrfAuthenticationStrategy.getBeanDefinition();
} }
void setIgnoreCsrfRequestMatchers(List<BeanDefinition> requestMatchers) {
if (!requestMatchers.isEmpty()) {
BeanMetadataElement requestMatcher;
if (StringUtils.hasText(this.requestMatcherRef)) {
requestMatcher = new RuntimeBeanReference(this.requestMatcherRef);
} else {
requestMatcher = new RootBeanDefinition(DefaultRequiresCsrfMatcher.class);
}
BeanDefinitionBuilder and = BeanDefinitionBuilder
.rootBeanDefinition(AndRequestMatcher.class);
BeanDefinitionBuilder negated = BeanDefinitionBuilder
.rootBeanDefinition(NegatedRequestMatcher.class);
BeanDefinitionBuilder or = BeanDefinitionBuilder
.rootBeanDefinition(OrRequestMatcher.class);
or.addConstructorArgValue(requestMatchers);
negated.addConstructorArgValue(or.getBeanDefinition());
List<BeanMetadataElement> ands = new ManagedList<>();
ands.add(requestMatcher);
ands.add(negated.getBeanDefinition());
and.addConstructorArgValue(ands);
this.csrfFilter.getPropertyValues()
.add("requireCsrfProtectionMatcher", and.getBeanDefinition());
}
}
private static final class DefaultRequiresCsrfMatcher implements RequestMatcher {
private final HashSet<String> allowedMethods = new HashSet<>(
Arrays.asList("GET", "HEAD", "TRACE", "OPTIONS"));
/*
* (non-Javadoc)
*
* @see
* org.springframework.security.web.util.matcher.RequestMatcher#matches(javax.
* servlet.http.HttpServletRequest)
*/
@Override
public boolean matches(HttpServletRequest request) {
return !this.allowedMethods.contains(request.getMethod());
}
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2020 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.
@ -238,6 +238,12 @@ class HttpConfigurationBuilder {
} }
} }
void setCsrfIgnoreRequestMatchers(List<BeanDefinition> requestMatchers) {
if (csrfParser != null) {
csrfParser.setIgnoreCsrfRequestMatchers(requestMatchers);
}
}
// Needed to account for placeholders // Needed to account for placeholders
static String createPath(String path, boolean lowerCase) { static String createPath(String path, boolean lowerCase) {
return lowerCase ? path.toLowerCase() : path; return lowerCase ? path.toLowerCase() : path;

View File

@ -15,8 +15,14 @@
*/ */
package org.springframework.security.config.http; package org.springframework.security.config.http;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Element;
import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference; import org.springframework.beans.factory.config.BeanReference;
@ -44,9 +50,6 @@ import org.springframework.security.web.PortResolverImpl;
import org.springframework.security.web.util.matcher.AnyRequestMatcher; import org.springframework.security.web.util.matcher.AnyRequestMatcher;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils; import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
import java.util.*;
/** /**
* Sets up HTTP security: filter stack and protected URLs. * Sets up HTTP security: filter stack and protected URLs.
@ -156,6 +159,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
httpBldr.setLogoutHandlers(authBldr.getLogoutHandlers()); httpBldr.setLogoutHandlers(authBldr.getLogoutHandlers());
httpBldr.setEntryPoint(authBldr.getEntryPointBean()); httpBldr.setEntryPoint(authBldr.getEntryPointBean());
httpBldr.setAccessDeniedHandler(authBldr.getAccessDeniedHandlerBean()); httpBldr.setAccessDeniedHandler(authBldr.getAccessDeniedHandlerBean());
httpBldr.setCsrfIgnoreRequestMatchers(authBldr.getCsrfIgnoreRequestMatchers());
authenticationProviders.addAll(authBldr.getProviders()); authenticationProviders.addAll(authBldr.getProviders());