ソースを参照

Add Csrf Ignore Configurability

Issue gh-5185
Josh Cummings 5 年 前
コミット
98a2ca3bbc

+ 16 - 11
config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java

@@ -15,8 +15,18 @@
  */
 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.LogFactory;
+import org.w3c.dom.Element;
+
 import org.springframework.beans.BeanMetadataElement;
 import org.springframework.beans.factory.config.BeanDefinition;
 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.StringUtils;
 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.BASIC_AUTH_FILTER;
@@ -160,11 +161,12 @@ final class AuthenticationConfigBuilder {
 	private BeanReference oauth2LoginAuthenticationProviderRef;
 	private BeanReference oauth2LoginOidcAuthenticationProviderRef;
 	private BeanDefinition oauth2LoginLinks;
-
 	private BeanDefinition authorizationRequestRedirectFilter;
 	private BeanDefinition authorizationCodeGrantFilter;
 	private BeanReference authorizationCodeAuthenticationProviderRef;
 
+	private final List<BeanDefinition> csrfIgnoreRequestMatchers = new ManagedList<>();
+
 	AuthenticationConfigBuilder(Element element, boolean forceAutoConfig,
 			ParserContext pc, SessionCreationPolicy sessionPolicy,
 			BeanReference requestCache, BeanReference authenticationManager,
@@ -194,7 +196,6 @@ final class AuthenticationConfigBuilder {
 		createLoginPageFilterIfNeeded();
 		createUserDetailsServiceFactory();
 		createExceptionTranslationFilter();
-
 	}
 
 	void createRememberMeFilter(BeanReference authenticationManager) {
@@ -708,6 +709,10 @@ final class AuthenticationConfigBuilder {
 		return accessDeniedHandler;
 	}
 
+	List<BeanDefinition> getCsrfIgnoreRequestMatchers() {
+		return csrfIgnoreRequestMatchers;
+	}
+
 	void createAnonymousFilter() {
 		Element anonymousElt = DomUtils.getChildElementByTagName(httpElt,
 				Elements.ANONYMOUS);

+ 59 - 5
config/src/main/java/org/springframework/security/config/http/CsrfBeanDefinitionParser.java

@@ -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");
  * you may not use this file except in compliance with the License.
@@ -15,12 +15,19 @@
  */
 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.springframework.beans.BeanMetadataElement;
 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.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.ManagedList;
 import org.springframework.beans.factory.support.ManagedMap;
 import org.springframework.beans.factory.support.RootBeanDefinition;
 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.session.InvalidSessionAccessDeniedHandler;
 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.StringUtils;
 
@@ -58,6 +69,8 @@ public class CsrfBeanDefinitionParser implements BeanDefinitionParser {
 	private String csrfRepositoryRef;
 	private BeanDefinition csrfFilter;
 
+	private String requestMatcherRef;
+
 	@Override
 	public BeanDefinition parse(Element element, ParserContext pc) {
 		boolean disabled = element != null
@@ -77,10 +90,9 @@ public class CsrfBeanDefinitionParser implements BeanDefinitionParser {
 			}
 		}
 
-		String matcherRef = null;
 		if (element != null) {
 			this.csrfRepositoryRef = element.getAttribute(ATT_REPOSITORY);
-			matcherRef = element.getAttribute(ATT_MATCHER);
+			this.requestMatcherRef = element.getAttribute(ATT_MATCHER);
 		}
 
 		if (!StringUtils.hasText(this.csrfRepositoryRef)) {
@@ -100,8 +112,8 @@ public class CsrfBeanDefinitionParser implements BeanDefinitionParser {
 				.rootBeanDefinition(CsrfFilter.class);
 		builder.addConstructorArgReference(this.csrfRepositoryRef);
 
-		if (StringUtils.hasText(matcherRef)) {
-			builder.addPropertyReference("requireCsrfProtectionMatcher", matcherRef);
+		if (StringUtils.hasText(this.requestMatcherRef)) {
+			builder.addPropertyReference("requireCsrfProtectionMatcher", this.requestMatcherRef);
 		}
 
 		this.csrfFilter = builder.getBeanDefinition();
@@ -172,4 +184,46 @@ public class CsrfBeanDefinitionParser implements BeanDefinitionParser {
 		csrfAuthenticationStrategy.addConstructorArgReference(this.csrfRepositoryRef);
 		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());
+		}
+	}
 }

+ 7 - 1
config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java

@@ -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");
  * 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
 	static String createPath(String path, boolean lowerCase) {
 		return lowerCase ? path.toLowerCase() : path;

+ 7 - 3
config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java

@@ -15,8 +15,14 @@
  */
 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.LogFactory;
+import org.w3c.dom.Element;
+
 import org.springframework.beans.BeanMetadataElement;
 import org.springframework.beans.factory.config.BeanDefinition;
 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.util.StringUtils;
 import org.springframework.util.xml.DomUtils;
-import org.w3c.dom.Element;
-
-import java.util.*;
 
 /**
  * Sets up HTTP security: filter stack and protected URLs.
@@ -156,6 +159,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
 		httpBldr.setLogoutHandlers(authBldr.getLogoutHandlers());
 		httpBldr.setEntryPoint(authBldr.getEntryPointBean());
 		httpBldr.setAccessDeniedHandler(authBldr.getAccessDeniedHandlerBean());
+		httpBldr.setCsrfIgnoreRequestMatchers(authBldr.getCsrfIgnoreRequestMatchers());
 
 		authenticationProviders.addAll(authBldr.getProviders());