Ver Fonte

Add Cross Origin Policies headers

Add DSL support for Cross-Origin-Opener-Policy, Cross-Origin-Embedder-Policy and Cross-Origin-Resource-Policy headers

Closes gh-9385, gh-10118
Marcus Da Coregio há 3 anos atrás
pai
commit
65426a40ec
38 ficheiros alterados com 2513 adições e 9 exclusões
  1. 228 1
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/HeadersConfigurer.java
  2. 82 1
      config/src/main/java/org/springframework/security/config/http/HeadersBeanDefinitionParser.java
  3. 185 1
      config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java
  4. 42 0
      config/src/main/kotlin/org/springframework/security/config/web/server/ServerCrossOriginEmbedderPolicyDsl.kt
  5. 42 0
      config/src/main/kotlin/org/springframework/security/config/web/server/ServerCrossOriginOpenerPolicyDsl.kt
  6. 42 0
      config/src/main/kotlin/org/springframework/security/config/web/server/ServerCrossOriginResourcePolicyDsl.kt
  7. 51 1
      config/src/main/kotlin/org/springframework/security/config/web/server/ServerHeadersDsl.kt
  8. 60 0
      config/src/main/kotlin/org/springframework/security/config/web/servlet/HeadersDsl.kt
  9. 43 0
      config/src/main/kotlin/org/springframework/security/config/web/servlet/headers/CrossOriginEmbedderPolicyDsl.kt
  10. 43 0
      config/src/main/kotlin/org/springframework/security/config/web/servlet/headers/CrossOriginOpenerPolicyDsl.kt
  11. 43 0
      config/src/main/kotlin/org/springframework/security/config/web/servlet/headers/CrossOriginResourcePolicyDsl.kt
  12. 22 1
      config/src/main/resources/org/springframework/security/config/spring-security-5.7.rnc
  13. 74 0
      config/src/main/resources/org/springframework/security/config/spring-security-5.7.xsd
  14. 77 1
      config/src/test/java/org/springframework/security/config/annotation/web/configurers/HeadersConfigurerTests.java
  15. 49 1
      config/src/test/java/org/springframework/security/config/http/HttpHeadersConfigTests.java
  16. 52 1
      config/src/test/java/org/springframework/security/config/web/server/HeaderSpecTests.java
  17. 59 0
      config/src/test/kotlin/org/springframework/security/config/web/server/ServerHeadersDslTests.kt
  18. 1 1
      config/src/test/kotlin/org/springframework/security/config/web/servlet/HeadersDslTests.kt
  19. 36 0
      config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithCrossOriginEmbedderPolicy.xml
  20. 36 0
      config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithCrossOriginOpenerPolicy.xml
  21. 38 0
      config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithCrossOriginPolicies.xml
  22. 36 0
      config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithCrossOriginResourcePolicy.xml
  23. 20 0
      docs/modules/ROOT/pages/features/exploits/headers.adoc
  24. 62 0
      docs/modules/ROOT/pages/reactive/exploits/headers.adoc
  25. 66 0
      docs/modules/ROOT/pages/servlet/appendix/namespace/http.adoc
  26. 61 0
      docs/modules/ROOT/pages/servlet/exploits/headers.adoc
  27. 84 0
      web/src/main/java/org/springframework/security/web/header/writers/CrossOriginEmbedderPolicyHeaderWriter.java
  28. 86 0
      web/src/main/java/org/springframework/security/web/header/writers/CrossOriginOpenerPolicyHeaderWriter.java
  29. 86 0
      web/src/main/java/org/springframework/security/web/header/writers/CrossOriginResourcePolicyHeaderWriter.java
  30. 78 0
      web/src/main/java/org/springframework/security/web/server/header/CrossOriginEmbedderPolicyServerHttpHeadersWriter.java
  31. 80 0
      web/src/main/java/org/springframework/security/web/server/header/CrossOriginOpenerPolicyServerHttpHeadersWriter.java
  32. 80 0
      web/src/main/java/org/springframework/security/web/server/header/CrossOriginResourcePolicyServerHttpHeadersWriter.java
  33. 80 0
      web/src/test/java/org/springframework/security/web/header/writers/CrossOriginEmbedderPolicyHeaderWriterTests.java
  34. 80 0
      web/src/test/java/org/springframework/security/web/header/writers/CrossOriginOpenerPolicyHeaderWriterTests.java
  35. 80 0
      web/src/test/java/org/springframework/security/web/header/writers/CrossOriginResourcePolicyHeaderWriterTests.java
  36. 76 0
      web/src/test/java/org/springframework/security/web/server/header/CrossOriginEmbedderPolicyServerHttpHeadersWriterTests.java
  37. 77 0
      web/src/test/java/org/springframework/security/web/server/header/CrossOriginOpenerPolicyServerHttpHeadersWriterTests.java
  38. 76 0
      web/src/test/java/org/springframework/security/web/server/header/CrossOriginResourcePolicyServerHttpHeadersWriterTests.java

+ 228 - 1
config/src/main/java/org/springframework/security/config/annotation/web/configurers/HeadersConfigurer.java

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2021 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.
@@ -31,6 +31,9 @@ import org.springframework.security.web.header.HeaderWriter;
 import org.springframework.security.web.header.HeaderWriterFilter;
 import org.springframework.security.web.header.HeaderWriterFilter;
 import org.springframework.security.web.header.writers.CacheControlHeadersWriter;
 import org.springframework.security.web.header.writers.CacheControlHeadersWriter;
 import org.springframework.security.web.header.writers.ContentSecurityPolicyHeaderWriter;
 import org.springframework.security.web.header.writers.ContentSecurityPolicyHeaderWriter;
+import org.springframework.security.web.header.writers.CrossOriginEmbedderPolicyHeaderWriter;
+import org.springframework.security.web.header.writers.CrossOriginOpenerPolicyHeaderWriter;
+import org.springframework.security.web.header.writers.CrossOriginResourcePolicyHeaderWriter;
 import org.springframework.security.web.header.writers.FeaturePolicyHeaderWriter;
 import org.springframework.security.web.header.writers.FeaturePolicyHeaderWriter;
 import org.springframework.security.web.header.writers.HpkpHeaderWriter;
 import org.springframework.security.web.header.writers.HpkpHeaderWriter;
 import org.springframework.security.web.header.writers.HstsHeaderWriter;
 import org.springframework.security.web.header.writers.HstsHeaderWriter;
@@ -97,6 +100,12 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
 
 
 	private final PermissionsPolicyConfig permissionsPolicy = new PermissionsPolicyConfig();
 	private final PermissionsPolicyConfig permissionsPolicy = new PermissionsPolicyConfig();
 
 
+	private final CrossOriginOpenerPolicyConfig crossOriginOpenerPolicy = new CrossOriginOpenerPolicyConfig();
+
+	private final CrossOriginEmbedderPolicyConfig crossOriginEmbedderPolicy = new CrossOriginEmbedderPolicyConfig();
+
+	private final CrossOriginResourcePolicyConfig crossOriginResourcePolicy = new CrossOriginResourcePolicyConfig();
+
 	/**
 	/**
 	 * Creates a new instance
 	 * Creates a new instance
 	 *
 	 *
@@ -392,6 +401,9 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
 		addIfNotNull(writers, this.referrerPolicy.writer);
 		addIfNotNull(writers, this.referrerPolicy.writer);
 		addIfNotNull(writers, this.featurePolicy.writer);
 		addIfNotNull(writers, this.featurePolicy.writer);
 		addIfNotNull(writers, this.permissionsPolicy.writer);
 		addIfNotNull(writers, this.permissionsPolicy.writer);
+		addIfNotNull(writers, this.crossOriginOpenerPolicy.writer);
+		addIfNotNull(writers, this.crossOriginEmbedderPolicy.writer);
+		addIfNotNull(writers, this.crossOriginResourcePolicy.writer);
 		writers.addAll(this.headerWriters);
 		writers.addAll(this.headerWriters);
 		return writers;
 		return writers;
 	}
 	}
@@ -544,6 +556,129 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
 		return this.permissionsPolicy;
 		return this.permissionsPolicy;
 	}
 	}
 
 
+	/**
+	 * Allows configuration for <a href=
+	 * "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
+	 * Cross-Origin-Opener-Policy</a> header.
+	 * <p>
+	 * Configuration is provided to the {@link CrossOriginOpenerPolicyHeaderWriter} which
+	 * responsible for writing the header.
+	 * </p>
+	 * @return the {@link CrossOriginOpenerPolicyConfig} for additional confniguration
+	 * @since 5.7
+	 * @see CrossOriginOpenerPolicyHeaderWriter
+	 */
+	public CrossOriginOpenerPolicyConfig crossOriginOpenerPolicy() {
+		this.crossOriginOpenerPolicy.writer = new CrossOriginOpenerPolicyHeaderWriter();
+		return this.crossOriginOpenerPolicy;
+	}
+
+	/**
+	 * Allows configuration for <a href=
+	 * "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
+	 * Cross-Origin-Opener-Policy</a> header.
+	 * <p>
+	 * Calling this method automatically enables (includes) the
+	 * {@code Cross-Origin-Opener-Policy} header in the response using the supplied
+	 * policy.
+	 * <p>
+	 * <p>
+	 * Configuration is provided to the {@link CrossOriginOpenerPolicyHeaderWriter} which
+	 * responsible for writing the header.
+	 * </p>
+	 * @return the {@link HeadersConfigurer} for additional customizations
+	 * @since 5.7
+	 * @see CrossOriginOpenerPolicyHeaderWriter
+	 */
+	public HeadersConfigurer<H> crossOriginOpenerPolicy(
+			Customizer<CrossOriginOpenerPolicyConfig> crossOriginOpenerPolicyCustomizer) {
+		this.crossOriginOpenerPolicy.writer = new CrossOriginOpenerPolicyHeaderWriter();
+		crossOriginOpenerPolicyCustomizer.customize(this.crossOriginOpenerPolicy);
+		return HeadersConfigurer.this;
+	}
+
+	/**
+	 * Allows configuration for <a href=
+	 * "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
+	 * Cross-Origin-Embedder-Policy</a> header.
+	 * <p>
+	 * Configuration is provided to the {@link CrossOriginEmbedderPolicyHeaderWriter}
+	 * which is responsible for writing the header.
+	 * </p>
+	 * @return the {@link CrossOriginEmbedderPolicyConfig} for additional customizations
+	 * @since 5.7
+	 * @see CrossOriginEmbedderPolicyHeaderWriter
+	 */
+	public CrossOriginEmbedderPolicyConfig crossOriginEmbedderPolicy() {
+		this.crossOriginEmbedderPolicy.writer = new CrossOriginEmbedderPolicyHeaderWriter();
+		return this.crossOriginEmbedderPolicy;
+	}
+
+	/**
+	 * Allows configuration for <a href=
+	 * "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
+	 * Cross-Origin-Embedder-Policy</a> header.
+	 * <p>
+	 * Calling this method automatically enables (includes) the
+	 * {@code Cross-Origin-Embedder-Policy} header in the response using the supplied
+	 * policy.
+	 * <p>
+	 * <p>
+	 * Configuration is provided to the {@link CrossOriginEmbedderPolicyHeaderWriter}
+	 * which is responsible for writing the header.
+	 * </p>
+	 * @return the {@link HeadersConfigurer} for additional customizations
+	 * @since 5.7
+	 * @see CrossOriginEmbedderPolicyHeaderWriter
+	 */
+	public HeadersConfigurer<H> crossOriginEmbedderPolicy(
+			Customizer<CrossOriginEmbedderPolicyConfig> crossOriginEmbedderPolicyCustomizer) {
+		this.crossOriginEmbedderPolicy.writer = new CrossOriginEmbedderPolicyHeaderWriter();
+		crossOriginEmbedderPolicyCustomizer.customize(this.crossOriginEmbedderPolicy);
+		return HeadersConfigurer.this;
+	}
+
+	/**
+	 * Allows configuration for <a href=
+	 * "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
+	 * Cross-Origin-Resource-Policy</a> header.
+	 * <p>
+	 * Configuration is provided to the {@link CrossOriginResourcePolicyHeaderWriter}
+	 * which is responsible for writing the header:
+	 * </p>
+	 * @return the {@link HeadersConfigurer} for additional customizations
+	 * @since 5.7
+	 * @see CrossOriginResourcePolicyHeaderWriter
+	 */
+	public CrossOriginResourcePolicyConfig crossOriginResourcePolicy() {
+		this.crossOriginResourcePolicy.writer = new CrossOriginResourcePolicyHeaderWriter();
+		return this.crossOriginResourcePolicy;
+	}
+
+	/**
+	 * Allows configuration for <a href=
+	 * "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
+	 * Cross-Origin-Resource-Policy</a> header.
+	 * <p>
+	 * Calling this method automatically enables (includes) the
+	 * {@code Cross-Origin-Resource-Policy} header in the response using the supplied
+	 * policy.
+	 * <p>
+	 * <p>
+	 * Configuration is provided to the {@link CrossOriginResourcePolicyHeaderWriter}
+	 * which is responsible for writing the header:
+	 * </p>
+	 * @return the {@link HeadersConfigurer} for additional customizations
+	 * @since 5.7
+	 * @see CrossOriginResourcePolicyHeaderWriter
+	 */
+	public HeadersConfigurer<H> crossOriginResourcePolicy(
+			Customizer<CrossOriginResourcePolicyConfig> crossOriginResourcePolicyCustomizer) {
+		this.crossOriginResourcePolicy.writer = new CrossOriginResourcePolicyHeaderWriter();
+		crossOriginResourcePolicyCustomizer.customize(this.crossOriginResourcePolicy);
+		return HeadersConfigurer.this;
+	}
+
 	public final class ContentTypeOptionsConfig {
 	public final class ContentTypeOptionsConfig {
 
 
 		private XContentTypeOptionsHeaderWriter writer;
 		private XContentTypeOptionsHeaderWriter writer;
@@ -1142,4 +1277,96 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
 
 
 	}
 	}
 
 
+	public final class CrossOriginOpenerPolicyConfig {
+
+		private CrossOriginOpenerPolicyHeaderWriter writer;
+
+		public CrossOriginOpenerPolicyConfig() {
+		}
+
+		/**
+		 * Sets the policy to be used in the {@code Cross-Origin-Opener-Policy} header
+		 * @param openerPolicy a {@code Cross-Origin-Opener-Policy}
+		 * @return the {@link CrossOriginOpenerPolicyConfig} for additional configuration
+		 * @throws IllegalArgumentException if openerPolicy is null
+		 */
+		public CrossOriginOpenerPolicyConfig policy(
+				CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy openerPolicy) {
+			this.writer.setPolicy(openerPolicy);
+			return this;
+		}
+
+		/**
+		 * Allows completing configuration of Cross Origin Opener Policy and continuing
+		 * configuration of headers.
+		 * @return the {@link HeadersConfigurer} for additional configuration
+		 */
+		public HeadersConfigurer<H> and() {
+			return HeadersConfigurer.this;
+		}
+
+	}
+
+	public final class CrossOriginEmbedderPolicyConfig {
+
+		private CrossOriginEmbedderPolicyHeaderWriter writer;
+
+		public CrossOriginEmbedderPolicyConfig() {
+		}
+
+		/**
+		 * Sets the policy to be used in the {@code Cross-Origin-Embedder-Policy} header
+		 * @param embedderPolicy a {@code Cross-Origin-Embedder-Policy}
+		 * @return the {@link CrossOriginEmbedderPolicyConfig} for additional
+		 * configuration
+		 * @throws IllegalArgumentException if embedderPolicy is null
+		 */
+		public CrossOriginEmbedderPolicyConfig policy(
+				CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy embedderPolicy) {
+			this.writer.setPolicy(embedderPolicy);
+			return this;
+		}
+
+		/**
+		 * Allows completing configuration of Cross-Origin-Embedder-Policy and continuing
+		 * configuration of headers.
+		 * @return the {@link HeadersConfigurer} for additional configuration
+		 */
+		public HeadersConfigurer<H> and() {
+			return HeadersConfigurer.this;
+		}
+
+	}
+
+	public final class CrossOriginResourcePolicyConfig {
+
+		private CrossOriginResourcePolicyHeaderWriter writer;
+
+		public CrossOriginResourcePolicyConfig() {
+		}
+
+		/**
+		 * Sets the policy to be used in the {@code Cross-Origin-Resource-Policy} header
+		 * @param resourcePolicy a {@code Cross-Origin-Resource-Policy}
+		 * @return the {@link CrossOriginResourcePolicyConfig} for additional
+		 * configuration
+		 * @throws IllegalArgumentException if resourcePolicy is null
+		 */
+		public CrossOriginResourcePolicyConfig policy(
+				CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy resourcePolicy) {
+			this.writer.setPolicy(resourcePolicy);
+			return this;
+		}
+
+		/**
+		 * Allows completing configuration of Cross-Origin-Resource-Policy and continuing
+		 * configuration of headers.
+		 * @return the {@link HeadersConfigurer} for additional configuration
+		 */
+		public HeadersConfigurer<H> and() {
+			return HeadersConfigurer.this;
+		}
+
+	}
+
 }
 }

+ 82 - 1
config/src/main/java/org/springframework/security/config/http/HeadersBeanDefinitionParser.java

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2021 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.
@@ -36,6 +36,9 @@ import org.springframework.beans.factory.xml.ParserContext;
 import org.springframework.security.web.header.HeaderWriterFilter;
 import org.springframework.security.web.header.HeaderWriterFilter;
 import org.springframework.security.web.header.writers.CacheControlHeadersWriter;
 import org.springframework.security.web.header.writers.CacheControlHeadersWriter;
 import org.springframework.security.web.header.writers.ContentSecurityPolicyHeaderWriter;
 import org.springframework.security.web.header.writers.ContentSecurityPolicyHeaderWriter;
+import org.springframework.security.web.header.writers.CrossOriginEmbedderPolicyHeaderWriter;
+import org.springframework.security.web.header.writers.CrossOriginOpenerPolicyHeaderWriter;
+import org.springframework.security.web.header.writers.CrossOriginResourcePolicyHeaderWriter;
 import org.springframework.security.web.header.writers.FeaturePolicyHeaderWriter;
 import org.springframework.security.web.header.writers.FeaturePolicyHeaderWriter;
 import org.springframework.security.web.header.writers.HpkpHeaderWriter;
 import org.springframework.security.web.header.writers.HpkpHeaderWriter;
 import org.springframework.security.web.header.writers.HstsHeaderWriter;
 import org.springframework.security.web.header.writers.HstsHeaderWriter;
@@ -122,6 +125,12 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
 
 
 	private static final String PERMISSIONS_POLICY_ELEMENT = "permissions-policy";
 	private static final String PERMISSIONS_POLICY_ELEMENT = "permissions-policy";
 
 
+	private static final String CROSS_ORIGIN_OPENER_POLICY_ELEMENT = "cross-origin-opener-policy";
+
+	private static final String CROSS_ORIGIN_EMBEDDER_POLICY_ELEMENT = "cross-origin-embedder-policy";
+
+	private static final String CROSS_ORIGIN_RESOURCE_POLICY_ELEMENT = "cross-origin-resource-policy";
+
 	private static final String ALLOW_FROM = "ALLOW-FROM";
 	private static final String ALLOW_FROM = "ALLOW-FROM";
 
 
 	private ManagedList<BeanMetadataElement> headerWriters;
 	private ManagedList<BeanMetadataElement> headerWriters;
@@ -144,6 +153,9 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
 		parseReferrerPolicyElement(element, parserContext);
 		parseReferrerPolicyElement(element, parserContext);
 		parseFeaturePolicyElement(element, parserContext);
 		parseFeaturePolicyElement(element, parserContext);
 		parsePermissionsPolicyElement(element, parserContext);
 		parsePermissionsPolicyElement(element, parserContext);
+		parseCrossOriginOpenerPolicy(disabled, element);
+		parseCrossOriginEmbedderPolicy(disabled, element);
+		parseCrossOriginResourcePolicy(disabled, element);
 		parseHeaderElements(element);
 		parseHeaderElements(element);
 		boolean noWriters = this.headerWriters.isEmpty();
 		boolean noWriters = this.headerWriters.isEmpty();
 		if (disabled && !noWriters) {
 		if (disabled && !noWriters) {
@@ -376,6 +388,75 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
 		this.headerWriters.add(headersWriter.getBeanDefinition());
 		this.headerWriters.add(headersWriter.getBeanDefinition());
 	}
 	}
 
 
+	private void parseCrossOriginOpenerPolicy(boolean elementDisabled, Element element) {
+		if (elementDisabled || element == null) {
+			return;
+		}
+		CrossOriginOpenerPolicyHeaderWriter writer = new CrossOriginOpenerPolicyHeaderWriter();
+		Element crossOriginOpenerPolicyElement = DomUtils.getChildElementByTagName(element,
+				CROSS_ORIGIN_OPENER_POLICY_ELEMENT);
+		if (crossOriginOpenerPolicyElement != null) {
+			addCrossOriginOpenerPolicy(crossOriginOpenerPolicyElement, writer);
+		}
+		BeanDefinitionBuilder builder = BeanDefinitionBuilder
+				.genericBeanDefinition(CrossOriginOpenerPolicyHeaderWriter.class, () -> writer);
+		this.headerWriters.add(builder.getBeanDefinition());
+	}
+
+	private void parseCrossOriginEmbedderPolicy(boolean elementDisabled, Element element) {
+		if (elementDisabled || element == null) {
+			return;
+		}
+		CrossOriginEmbedderPolicyHeaderWriter writer = new CrossOriginEmbedderPolicyHeaderWriter();
+		Element crossOriginEmbedderPolicyElement = DomUtils.getChildElementByTagName(element,
+				CROSS_ORIGIN_EMBEDDER_POLICY_ELEMENT);
+		if (crossOriginEmbedderPolicyElement != null) {
+			addCrossOriginEmbedderPolicy(crossOriginEmbedderPolicyElement, writer);
+		}
+		BeanDefinitionBuilder builder = BeanDefinitionBuilder
+				.genericBeanDefinition(CrossOriginEmbedderPolicyHeaderWriter.class, () -> writer);
+		this.headerWriters.add(builder.getBeanDefinition());
+	}
+
+	private void parseCrossOriginResourcePolicy(boolean elementDisabled, Element element) {
+		if (elementDisabled || element == null) {
+			return;
+		}
+		CrossOriginResourcePolicyHeaderWriter writer = new CrossOriginResourcePolicyHeaderWriter();
+		Element crossOriginResourcePolicyElement = DomUtils.getChildElementByTagName(element,
+				CROSS_ORIGIN_RESOURCE_POLICY_ELEMENT);
+		if (crossOriginResourcePolicyElement != null) {
+			addCrossOriginResourcePolicy(crossOriginResourcePolicyElement, writer);
+		}
+		BeanDefinitionBuilder builder = BeanDefinitionBuilder
+				.genericBeanDefinition(CrossOriginResourcePolicyHeaderWriter.class, () -> writer);
+		this.headerWriters.add(builder.getBeanDefinition());
+	}
+
+	private void addCrossOriginResourcePolicy(Element crossOriginResourcePolicyElement,
+			CrossOriginResourcePolicyHeaderWriter writer) {
+		String policy = crossOriginResourcePolicyElement.getAttribute(ATT_POLICY);
+		if (StringUtils.hasText(policy)) {
+			writer.setPolicy(CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy.from(policy));
+		}
+	}
+
+	private void addCrossOriginEmbedderPolicy(Element crossOriginEmbedderPolicyElement,
+			CrossOriginEmbedderPolicyHeaderWriter writer) {
+		String policy = crossOriginEmbedderPolicyElement.getAttribute(ATT_POLICY);
+		if (StringUtils.hasText(policy)) {
+			writer.setPolicy(CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy.from(policy));
+		}
+	}
+
+	private void addCrossOriginOpenerPolicy(Element crossOriginOpenerPolicyElement,
+			CrossOriginOpenerPolicyHeaderWriter writer) {
+		String policy = crossOriginOpenerPolicyElement.getAttribute(ATT_POLICY);
+		if (StringUtils.hasText(policy)) {
+			writer.setPolicy(CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy.from(policy));
+		}
+	}
+
 	private void attrNotAllowed(ParserContext context, String attrName, String otherAttrName, Element element) {
 	private void attrNotAllowed(ParserContext context, String attrName, String otherAttrName, Element element) {
 		context.getReaderContext().error("Only one of '" + attrName + "' or '" + otherAttrName + "' can be set.",
 		context.getReaderContext().error("Only one of '" + attrName + "' or '" + otherAttrName + "' can be set.",
 				element);
 				element);

+ 185 - 1
config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java

@@ -149,6 +149,12 @@ import org.springframework.security.web.server.header.CacheControlServerHttpHead
 import org.springframework.security.web.server.header.CompositeServerHttpHeadersWriter;
 import org.springframework.security.web.server.header.CompositeServerHttpHeadersWriter;
 import org.springframework.security.web.server.header.ContentSecurityPolicyServerHttpHeadersWriter;
 import org.springframework.security.web.server.header.ContentSecurityPolicyServerHttpHeadersWriter;
 import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter;
 import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter;
+import org.springframework.security.web.server.header.CrossOriginEmbedderPolicyServerHttpHeadersWriter;
+import org.springframework.security.web.server.header.CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy;
+import org.springframework.security.web.server.header.CrossOriginOpenerPolicyServerHttpHeadersWriter;
+import org.springframework.security.web.server.header.CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy;
+import org.springframework.security.web.server.header.CrossOriginResourcePolicyServerHttpHeadersWriter;
+import org.springframework.security.web.server.header.CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy;
 import org.springframework.security.web.server.header.FeaturePolicyServerHttpHeadersWriter;
 import org.springframework.security.web.server.header.FeaturePolicyServerHttpHeadersWriter;
 import org.springframework.security.web.server.header.HttpHeaderWriterWebFilter;
 import org.springframework.security.web.server.header.HttpHeaderWriterWebFilter;
 import org.springframework.security.web.server.header.PermissionsPolicyServerHttpHeadersWriter;
 import org.springframework.security.web.server.header.PermissionsPolicyServerHttpHeadersWriter;
@@ -2380,10 +2386,17 @@ public class ServerHttpSecurity {
 
 
 		private ReferrerPolicyServerHttpHeadersWriter referrerPolicy = new ReferrerPolicyServerHttpHeadersWriter();
 		private ReferrerPolicyServerHttpHeadersWriter referrerPolicy = new ReferrerPolicyServerHttpHeadersWriter();
 
 
+		private CrossOriginOpenerPolicyServerHttpHeadersWriter crossOriginOpenerPolicy = new CrossOriginOpenerPolicyServerHttpHeadersWriter();
+
+		private CrossOriginEmbedderPolicyServerHttpHeadersWriter crossOriginEmbedderPolicy = new CrossOriginEmbedderPolicyServerHttpHeadersWriter();
+
+		private CrossOriginResourcePolicyServerHttpHeadersWriter crossOriginResourcePolicy = new CrossOriginResourcePolicyServerHttpHeadersWriter();
+
 		private HeaderSpec() {
 		private HeaderSpec() {
 			this.writers = new ArrayList<>(Arrays.asList(this.cacheControl, this.contentTypeOptions, this.hsts,
 			this.writers = new ArrayList<>(Arrays.asList(this.cacheControl, this.contentTypeOptions, this.hsts,
 					this.frameOptions, this.xss, this.featurePolicy, this.permissionsPolicy, this.contentSecurityPolicy,
 					this.frameOptions, this.xss, this.featurePolicy, this.permissionsPolicy, this.contentSecurityPolicy,
-					this.referrerPolicy));
+					this.referrerPolicy, this.crossOriginOpenerPolicy, this.crossOriginEmbedderPolicy,
+					this.crossOriginResourcePolicy));
 		}
 		}
 
 
 		/**
 		/**
@@ -2595,6 +2608,84 @@ public class ServerHttpSecurity {
 			return this;
 			return this;
 		}
 		}
 
 
+		/**
+		 * Configures the <a href=
+		 * "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
+		 * Cross-Origin-Opener-Policy</a> header.
+		 * @return the {@link CrossOriginOpenerPolicySpec} to configure
+		 * @since 5.7
+		 * @see CrossOriginOpenerPolicyServerHttpHeadersWriter
+		 */
+		public CrossOriginOpenerPolicySpec crossOriginOpenerPolicy() {
+			return new CrossOriginOpenerPolicySpec();
+		}
+
+		/**
+		 * Configures the <a href=
+		 * "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
+		 * Cross-Origin-Opener-Policy</a> header.
+		 * @return the {@link HeaderSpec} to customize
+		 * @since 5.7
+		 * @see CrossOriginOpenerPolicyServerHttpHeadersWriter
+		 */
+		public HeaderSpec crossOriginOpenerPolicy(
+				Customizer<CrossOriginOpenerPolicySpec> crossOriginOpenerPolicyCustomizer) {
+			crossOriginOpenerPolicyCustomizer.customize(new CrossOriginOpenerPolicySpec());
+			return this;
+		}
+
+		/**
+		 * Configures the <a href=
+		 * "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
+		 * Cross-Origin-Embedder-Policy</a> header.
+		 * @return the {@link CrossOriginEmbedderPolicySpec} to configure
+		 * @since 5.7
+		 * @see CrossOriginEmbedderPolicyServerHttpHeadersWriter
+		 */
+		public CrossOriginEmbedderPolicySpec crossOriginEmbedderPolicy() {
+			return new CrossOriginEmbedderPolicySpec();
+		}
+
+		/**
+		 * Configures the <a href=
+		 * "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
+		 * Cross-Origin-Embedder-Policy</a> header.
+		 * @return the {@link HeaderSpec} to customize
+		 * @since 5.7
+		 * @see CrossOriginEmbedderPolicyServerHttpHeadersWriter
+		 */
+		public HeaderSpec crossOriginEmbedderPolicy(
+				Customizer<CrossOriginEmbedderPolicySpec> crossOriginEmbedderPolicyCustomizer) {
+			crossOriginEmbedderPolicyCustomizer.customize(new CrossOriginEmbedderPolicySpec());
+			return this;
+		}
+
+		/**
+		 * Configures the <a href=
+		 * "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
+		 * Cross-Origin-Resource-Policy</a> header.
+		 * @return the {@link CrossOriginResourcePolicySpec} to configure
+		 * @since 5.7
+		 * @see CrossOriginResourcePolicyServerHttpHeadersWriter
+		 */
+		public CrossOriginResourcePolicySpec crossOriginResourcePolicy() {
+			return new CrossOriginResourcePolicySpec();
+		}
+
+		/**
+		 * Configures the <a href=
+		 * "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
+		 * Cross-Origin-Resource-Policy</a> header.
+		 * @return the {@link HeaderSpec} to customize
+		 * @since 5.7
+		 * @see CrossOriginResourcePolicyServerHttpHeadersWriter
+		 */
+		public HeaderSpec crossOriginResourcePolicy(
+				Customizer<CrossOriginResourcePolicySpec> crossOriginResourcePolicyCustomizer) {
+			crossOriginResourcePolicyCustomizer.customize(new CrossOriginResourcePolicySpec());
+			return this;
+		}
+
 		/**
 		/**
 		 * Configures cache control headers
 		 * Configures cache control headers
 		 *
 		 *
@@ -2910,6 +3001,99 @@ public class ServerHttpSecurity {
 
 
 		}
 		}
 
 
+		/**
+		 * Configures the Cross-Origin-Opener-Policy header
+		 *
+		 * @since 5.7
+		 */
+		public final class CrossOriginOpenerPolicySpec {
+
+			private CrossOriginOpenerPolicySpec() {
+			}
+
+			/**
+			 * Sets the value to be used in the `Cross-Origin-Opener-Policy` header
+			 * @param openerPolicy a opener policy
+			 * @return the {@link CrossOriginOpenerPolicySpec} to continue configuring
+			 */
+			public CrossOriginOpenerPolicySpec policy(CrossOriginOpenerPolicy openerPolicy) {
+				HeaderSpec.this.crossOriginOpenerPolicy.setPolicy(openerPolicy);
+				return this;
+			}
+
+			/**
+			 * Allows method chaining to continue configuring the
+			 * {@link ServerHttpSecurity}.
+			 * @return the {@link HeaderSpec} to continue configuring
+			 */
+			public HeaderSpec and() {
+				return HeaderSpec.this;
+			}
+
+		}
+
+		/**
+		 * Configures the Cross-Origin-Embedder-Policy header
+		 *
+		 * @since 5.7
+		 */
+		public final class CrossOriginEmbedderPolicySpec {
+
+			private CrossOriginEmbedderPolicySpec() {
+			}
+
+			/**
+			 * Sets the value to be used in the `Cross-Origin-Embedder-Policy` header
+			 * @param embedderPolicy a opener policy
+			 * @return the {@link CrossOriginEmbedderPolicySpec} to continue configuring
+			 */
+			public CrossOriginEmbedderPolicySpec policy(CrossOriginEmbedderPolicy embedderPolicy) {
+				HeaderSpec.this.crossOriginEmbedderPolicy.setPolicy(embedderPolicy);
+				return this;
+			}
+
+			/**
+			 * Allows method chaining to continue configuring the
+			 * {@link ServerHttpSecurity}.
+			 * @return the {@link HeaderSpec} to continue configuring
+			 */
+			public HeaderSpec and() {
+				return HeaderSpec.this;
+			}
+
+		}
+
+		/**
+		 * Configures the Cross-Origin-Resource-Policy header
+		 *
+		 * @since 5.7
+		 */
+		public final class CrossOriginResourcePolicySpec {
+
+			private CrossOriginResourcePolicySpec() {
+			}
+
+			/**
+			 * Sets the value to be used in the `Cross-Origin-Resource-Policy` header
+			 * @param resourcePolicy a opener policy
+			 * @return the {@link CrossOriginResourcePolicySpec} to continue configuring
+			 */
+			public CrossOriginResourcePolicySpec policy(CrossOriginResourcePolicy resourcePolicy) {
+				HeaderSpec.this.crossOriginResourcePolicy.setPolicy(resourcePolicy);
+				return this;
+			}
+
+			/**
+			 * Allows method chaining to continue configuring the
+			 * {@link ServerHttpSecurity}.
+			 * @return the {@link HeaderSpec} to continue configuring
+			 */
+			public HeaderSpec and() {
+				return HeaderSpec.this;
+			}
+
+		}
+
 	}
 	}
 
 
 	/**
 	/**

+ 42 - 0
config/src/main/kotlin/org/springframework/security/config/web/server/ServerCrossOriginEmbedderPolicyDsl.kt

@@ -0,0 +1,42 @@
+/*
+ * Copyright 2002-2021 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
+ *
+ *      https://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.config.web.server
+
+import org.springframework.security.config.annotation.web.builders.HttpSecurity
+import org.springframework.security.web.server.header.CrossOriginEmbedderPolicyServerHttpHeadersWriter
+
+/**
+ * A Kotlin DSL to configure the [HttpSecurity] Cross-Origin-Embedder-Policy header using
+ * idiomatic Kotlin code.
+ *
+ * @author Marcus Da Coregio
+ * @since 5.7
+ * @property policy the policy to be used in the response header.
+ */
+@ServerSecurityMarker
+class ServerCrossOriginEmbedderPolicyDsl {
+
+    var policy: CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy? = null
+
+    internal fun get(): (ServerHttpSecurity.HeaderSpec.CrossOriginEmbedderPolicySpec) -> Unit {
+        return { crossOriginEmbedderPolicy ->
+            policy?.also {
+                crossOriginEmbedderPolicy.policy(policy)
+            }
+        }
+    }
+}

+ 42 - 0
config/src/main/kotlin/org/springframework/security/config/web/server/ServerCrossOriginOpenerPolicyDsl.kt

@@ -0,0 +1,42 @@
+/*
+ * Copyright 2002-2021 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
+ *
+ *      https://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.config.web.server
+
+import org.springframework.security.config.annotation.web.builders.HttpSecurity
+import org.springframework.security.web.server.header.CrossOriginOpenerPolicyServerHttpHeadersWriter
+
+/**
+ * A Kotlin DSL to configure the [HttpSecurity] Cross-Origin-Opener-Policy header using
+ * idiomatic Kotlin code.
+ *
+ * @author Marcus Da Coregio
+ * @since 5.7
+ * @property policy the policy to be used in the response header.
+ */
+@ServerSecurityMarker
+class ServerCrossOriginOpenerPolicyDsl {
+
+    var policy: CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy? = null
+
+    internal fun get(): (ServerHttpSecurity.HeaderSpec.CrossOriginOpenerPolicySpec) -> Unit {
+        return { crossOriginOpenerPolicy ->
+            policy?.also {
+                crossOriginOpenerPolicy.policy(policy)
+            }
+        }
+    }
+}

+ 42 - 0
config/src/main/kotlin/org/springframework/security/config/web/server/ServerCrossOriginResourcePolicyDsl.kt

@@ -0,0 +1,42 @@
+/*
+ * Copyright 2002-2021 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
+ *
+ *      https://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.config.web.server
+
+import org.springframework.security.config.annotation.web.builders.HttpSecurity
+import org.springframework.security.web.server.header.CrossOriginResourcePolicyServerHttpHeadersWriter
+
+/**
+ * A Kotlin DSL to configure the [HttpSecurity] Cross-Origin-Resource-Policy header using
+ * idiomatic Kotlin code.
+ *
+ * @author Marcus Da Coregio
+ * @since 5.7
+ * @property policy the policy to be used in the response header.
+ */
+@ServerSecurityMarker
+class ServerCrossOriginResourcePolicyDsl {
+
+    var policy: CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy? = null
+
+    internal fun get(): (ServerHttpSecurity.HeaderSpec.CrossOriginResourcePolicySpec) -> Unit {
+        return { crossOriginResourcePolicy ->
+            policy?.also {
+                crossOriginResourcePolicy.policy(policy)
+            }
+        }
+    }
+}

+ 51 - 1
config/src/main/kotlin/org/springframework/security/config/web/server/ServerHeadersDsl.kt

@@ -16,7 +16,12 @@
 
 
 package org.springframework.security.config.web.server
 package org.springframework.security.config.web.server
 
 
-import org.springframework.security.web.server.header.*
+import org.springframework.security.web.server.header.CacheControlServerHttpHeadersWriter
+import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter
+import org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter
+import org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter
+import org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter
+import org.springframework.security.web.server.header.XXssProtectionServerHttpHeadersWriter
 
 
 /**
 /**
  * A Kotlin DSL to configure [ServerHttpSecurity] headers using idiomatic Kotlin code.
  * A Kotlin DSL to configure [ServerHttpSecurity] headers using idiomatic Kotlin code.
@@ -35,6 +40,9 @@ class ServerHeadersDsl {
     private var referrerPolicy: ((ServerHttpSecurity.HeaderSpec.ReferrerPolicySpec) -> Unit)? = null
     private var referrerPolicy: ((ServerHttpSecurity.HeaderSpec.ReferrerPolicySpec) -> Unit)? = null
     private var featurePolicyDirectives: String? = null
     private var featurePolicyDirectives: String? = null
     private var permissionsPolicy: ((ServerHttpSecurity.HeaderSpec.PermissionsPolicySpec) -> Unit)? = null
     private var permissionsPolicy: ((ServerHttpSecurity.HeaderSpec.PermissionsPolicySpec) -> Unit)? = null
+    private var crossOriginOpenerPolicy: ((ServerHttpSecurity.HeaderSpec.CrossOriginOpenerPolicySpec) -> Unit)? = null
+    private var crossOriginEmbedderPolicy: ((ServerHttpSecurity.HeaderSpec.CrossOriginEmbedderPolicySpec) -> Unit)? = null
+    private var crossOriginResourcePolicy: ((ServerHttpSecurity.HeaderSpec.CrossOriginResourcePolicySpec) -> Unit)? = null
 
 
     private var disabled = false
     private var disabled = false
 
 
@@ -157,6 +165,39 @@ class ServerHeadersDsl {
         this.permissionsPolicy = ServerPermissionsPolicyDsl().apply(permissionsPolicyConfig).get()
         this.permissionsPolicy = ServerPermissionsPolicyDsl().apply(permissionsPolicyConfig).get()
     }
     }
 
 
+    /**
+     * Allows configuration for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
+     * Cross-Origin-Opener-Policy</a> header.
+     *
+     * @since 5.7
+     * @param crossOriginOpenerPolicyConfig the customization to apply to the header
+     */
+    fun crossOriginOpenerPolicy(crossOriginOpenerPolicyConfig: ServerCrossOriginOpenerPolicyDsl.() -> Unit) {
+        this.crossOriginOpenerPolicy = ServerCrossOriginOpenerPolicyDsl().apply(crossOriginOpenerPolicyConfig).get()
+    }
+
+    /**
+     * Allows configuration for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
+     * Cross-Origin-Embedder-Policy</a> header.
+     *
+     * @since 5.7
+     * @param crossOriginEmbedderPolicyConfig the customization to apply to the header
+     */
+    fun crossOriginEmbedderPolicy(crossOriginEmbedderPolicyConfig: ServerCrossOriginEmbedderPolicyDsl.() -> Unit) {
+        this.crossOriginEmbedderPolicy = ServerCrossOriginEmbedderPolicyDsl().apply(crossOriginEmbedderPolicyConfig).get()
+    }
+
+    /**
+     * Allows configuration for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
+     * Cross-Origin-Resource-Policy</a> header.
+     *
+     * @since 5.7
+     * @param crossOriginResourcePolicyConfig the customization to apply to the header
+     */
+    fun crossOriginResourcePolicy(crossOriginResourcePolicyConfig: ServerCrossOriginResourcePolicyDsl.() -> Unit) {
+        this.crossOriginResourcePolicy = ServerCrossOriginResourcePolicyDsl().apply(crossOriginResourcePolicyConfig).get()
+    }
+
     /**
     /**
      * Disables HTTP response headers.
      * Disables HTTP response headers.
      */
      */
@@ -194,6 +235,15 @@ class ServerHeadersDsl {
             referrerPolicy?.also {
             referrerPolicy?.also {
                 headers.referrerPolicy(referrerPolicy)
                 headers.referrerPolicy(referrerPolicy)
             }
             }
+            crossOriginOpenerPolicy?.also {
+                headers.crossOriginOpenerPolicy(crossOriginOpenerPolicy)
+            }
+            crossOriginEmbedderPolicy?.also {
+                headers.crossOriginEmbedderPolicy(crossOriginEmbedderPolicy)
+            }
+            crossOriginResourcePolicy?.also {
+                headers.crossOriginResourcePolicy(crossOriginResourcePolicy)
+            }
             if (disabled) {
             if (disabled) {
                 headers.disable()
                 headers.disable()
             }
             }

+ 60 - 0
config/src/main/kotlin/org/springframework/security/config/web/servlet/HeadersDsl.kt

@@ -42,6 +42,9 @@ class HeadersDsl {
     private var referrerPolicy: ((HeadersConfigurer<HttpSecurity>.ReferrerPolicyConfig) -> Unit)? = null
     private var referrerPolicy: ((HeadersConfigurer<HttpSecurity>.ReferrerPolicyConfig) -> Unit)? = null
     private var featurePolicyDirectives: String? = null
     private var featurePolicyDirectives: String? = null
     private var permissionsPolicy: ((HeadersConfigurer<HttpSecurity>.PermissionsPolicyConfig) -> Unit)? = null
     private var permissionsPolicy: ((HeadersConfigurer<HttpSecurity>.PermissionsPolicyConfig) -> Unit)? = null
+    private var crossOriginOpenerPolicy: ((HeadersConfigurer<HttpSecurity>.CrossOriginOpenerPolicyConfig) -> Unit)? = null
+    private var crossOriginEmbedderPolicy: ((HeadersConfigurer<HttpSecurity>.CrossOriginEmbedderPolicyConfig) -> Unit)? = null
+    private var crossOriginResourcePolicy: ((HeadersConfigurer<HttpSecurity>.CrossOriginResourcePolicyConfig) -> Unit)? = null
     private var disabled = false
     private var disabled = false
     private var headerWriters = mutableListOf<HeaderWriter>()
     private var headerWriters = mutableListOf<HeaderWriter>()
 
 
@@ -181,6 +184,54 @@ class HeadersDsl {
         this.permissionsPolicy = PermissionsPolicyDsl().apply(permissionsPolicyConfig).get()
         this.permissionsPolicy = PermissionsPolicyDsl().apply(permissionsPolicyConfig).get()
     }
     }
 
 
+    /**
+     * Allows configuration for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
+     * Cross-Origin-Opener-Policy</a> header.
+     *
+     * <p>
+     * Calling this method automatically enables (includes) the Cross-Origin-Opener-Policy
+     * header in the response using the supplied policy.
+     * <p>
+     *
+     * @since 5.7
+     * @param crossOriginOpenerPolicyConfig the customization to apply to the header
+     */
+    fun crossOriginOpenerPolicy(crossOriginOpenerPolicyConfig: CrossOriginOpenerPolicyDsl.() -> Unit) {
+        this.crossOriginOpenerPolicy = CrossOriginOpenerPolicyDsl().apply(crossOriginOpenerPolicyConfig).get()
+    }
+
+    /**
+     * Allows configuration for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
+     * Cross-Origin-Embedder-Policy</a> header.
+     *
+     * <p>
+     * Calling this method automatically enables (includes) the Cross-Origin-Embedder-Policy
+     * header in the response using the supplied policy.
+     * <p>
+     *
+     * @since 5.7
+     * @param crossOriginEmbedderPolicyConfig the customization to apply to the header
+     */
+    fun crossOriginEmbedderPolicy(crossOriginEmbedderPolicyConfig: CrossOriginEmbedderPolicyDsl.() -> Unit) {
+        this.crossOriginEmbedderPolicy = CrossOriginEmbedderPolicyDsl().apply(crossOriginEmbedderPolicyConfig).get()
+    }
+
+    /**
+     * Configures the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
+     * Cross-Origin-Resource-Policy</a> header.
+     *
+     * <p>
+     * Calling this method automatically enables (includes) the Cross-Origin-Resource-Policy
+     * header in the response using the supplied policy.
+     * <p>
+     *
+     * @since 5.7
+     * @param crossOriginResourcePolicyConfig the customization to apply to the header
+     */
+    fun crossOriginResourcePolicy(crossOriginResourcePolicyConfig: CrossOriginResourcePolicyDsl.() -> Unit) {
+        this.crossOriginResourcePolicy = CrossOriginResourcePolicyDsl().apply(crossOriginResourcePolicyConfig).get()
+    }
+
     /**
     /**
      * Adds a [HeaderWriter] instance.
      * Adds a [HeaderWriter] instance.
      *
      *
@@ -238,6 +289,15 @@ class HeadersDsl {
             permissionsPolicy?.also {
             permissionsPolicy?.also {
                 headers.permissionsPolicy(permissionsPolicy)
                 headers.permissionsPolicy(permissionsPolicy)
             }
             }
+            crossOriginOpenerPolicy?.also {
+                headers.crossOriginOpenerPolicy(crossOriginOpenerPolicy)
+            }
+            crossOriginEmbedderPolicy?.also {
+                headers.crossOriginEmbedderPolicy(crossOriginEmbedderPolicy)
+            }
+            crossOriginResourcePolicy?.also {
+                headers.crossOriginResourcePolicy(crossOriginResourcePolicy)
+            }
             headerWriters.forEach { headerWriter ->
             headerWriters.forEach { headerWriter ->
                 headers.addHeaderWriter(headerWriter)
                 headers.addHeaderWriter(headerWriter)
             }
             }

+ 43 - 0
config/src/main/kotlin/org/springframework/security/config/web/servlet/headers/CrossOriginEmbedderPolicyDsl.kt

@@ -0,0 +1,43 @@
+/*
+ * Copyright 2002-2021 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
+ *
+ *      https://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.config.web.servlet.headers
+
+import org.springframework.security.config.annotation.web.builders.HttpSecurity
+import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer
+import org.springframework.security.web.header.writers.CrossOriginEmbedderPolicyHeaderWriter
+
+/**
+ * A Kotlin DSL to configure the [HttpSecurity] Cross-Origin-Embedder-Policy header using
+ * idiomatic Kotlin code.
+ *
+ * @author Marcus Da Coregio
+ * @since 5.7
+ * @property policy the policy to be used in the response header.
+ */
+@HeadersSecurityMarker
+class CrossOriginEmbedderPolicyDsl {
+
+    var policy: CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy? = null
+
+    internal fun get(): (HeadersConfigurer<HttpSecurity>.CrossOriginEmbedderPolicyConfig) -> Unit {
+        return { crossOriginEmbedderPolicy ->
+            policy?.also {
+                crossOriginEmbedderPolicy.policy(policy)
+            }
+        }
+    }
+}

+ 43 - 0
config/src/main/kotlin/org/springframework/security/config/web/servlet/headers/CrossOriginOpenerPolicyDsl.kt

@@ -0,0 +1,43 @@
+/*
+ * Copyright 2002-2021 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
+ *
+ *      https://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.config.web.servlet.headers
+
+import org.springframework.security.config.annotation.web.builders.HttpSecurity
+import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer
+import org.springframework.security.web.header.writers.CrossOriginOpenerPolicyHeaderWriter
+
+/**
+ * A Kotlin DSL to configure the [HttpSecurity] Cross-Origin-Opener-Policy header using
+ * idiomatic Kotlin code.
+ *
+ * @author Marcus Da Coregio
+ * @since 5.7
+ * @property policy the policy to be used in the response header.
+ */
+@HeadersSecurityMarker
+class CrossOriginOpenerPolicyDsl {
+
+    var policy: CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy? = null
+
+    internal fun get(): (HeadersConfigurer<HttpSecurity>.CrossOriginOpenerPolicyConfig) -> Unit {
+        return { crossOriginOpenerPolicy ->
+            policy?.also {
+                crossOriginOpenerPolicy.policy(policy)
+            }
+        }
+    }
+}

+ 43 - 0
config/src/main/kotlin/org/springframework/security/config/web/servlet/headers/CrossOriginResourcePolicyDsl.kt

@@ -0,0 +1,43 @@
+/*
+ * Copyright 2002-2021 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
+ *
+ *      https://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.config.web.servlet.headers
+
+import org.springframework.security.config.annotation.web.builders.HttpSecurity
+import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer
+import org.springframework.security.web.header.writers.CrossOriginResourcePolicyHeaderWriter
+
+/**
+ * A Kotlin DSL to configure the [HttpSecurity] Cross-Origin-Resource-Policy header using
+ * idiomatic Kotlin code.
+ *
+ * @author Marcus Da Coregio
+ * @since 5.7
+ * @property policy the policy to be used in the response header.
+ */
+@HeadersSecurityMarker
+class CrossOriginResourcePolicyDsl {
+
+    var policy: CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy? = null
+
+    internal fun get(): (HeadersConfigurer<HttpSecurity>.CrossOriginResourcePolicyConfig) -> Unit {
+        return { crossOriginResourcePolicy ->
+            policy?.also {
+                crossOriginResourcePolicy.policy(policy)
+            }
+        }
+    }
+}

+ 22 - 1
config/src/main/resources/org/springframework/security/config/spring-security-5.7.rnc

@@ -943,7 +943,7 @@ csrf-options.attlist &=
 
 
 headers =
 headers =
 ## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.
 ## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.
-element headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & feature-policy? & permissions-policy? & header*)}
+element headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & feature-policy? & permissions-policy? & cross-origin-opener-policy? & cross-origin-embedder-policy? & cross-origin-resource-policy? & header*)}
 headers-options.attlist &=
 headers-options.attlist &=
 	## Specifies if the default headers should be disabled. Default false.
 	## Specifies if the default headers should be disabled. Default false.
 	attribute defaults-disabled {xsd:token}?
 	attribute defaults-disabled {xsd:token}?
@@ -1092,6 +1092,27 @@ content-type-options.attlist &=
 	## If disabled, the X-Content-Type-Options header will not be included. Default false.
 	## If disabled, the X-Content-Type-Options header will not be included. Default false.
 	attribute disabled {xsd:boolean}?
 	attribute disabled {xsd:boolean}?
 
 
+cross-origin-opener-policy =
+	## Adds support for Cross-Origin-Opener-Policy header
+	element cross-origin-opener-policy {cross-origin-opener-policy-options.attlist,empty}
+cross-origin-opener-policy-options.attlist &=
+	## The policies for the Cross-Origin-Opener-Policy header.
+	attribute policy {"unsafe-none","same-origin","same-origin-allow-popups"}?
+
+cross-origin-embedder-policy =
+	## Adds support for Cross-Origin-Embedder-Policy header
+	element cross-origin-embedder-policy {cross-origin-embedder-policy-options.attlist,empty}
+cross-origin-embedder-policy-options.attlist &=
+	## The policies for the Cross-Origin-Embedder-Policy header.
+	attribute policy {"unsafe-none","require-corp"}?
+
+cross-origin-resource-policy =
+	## Adds support for Cross-Origin-Resource-Policy header
+	element cross-origin-resource-policy {cross-origin-resource-policy-options.attlist,empty}
+cross-origin-resource-policy-options.attlist &=
+	## The policies for the Cross-Origin-Resource-Policy header.
+	attribute policy {"cross-origin","same-origin","same-site"}?
+
 header=
 header=
 	## Add additional headers to the response.
 	## Add additional headers to the response.
 	element header {header.attlist}
 	element header {header.attlist}

+ 74 - 0
config/src/main/resources/org/springframework/security/config/spring-security-5.7.xsd

@@ -2768,6 +2768,9 @@
             <xs:element ref="security:referrer-policy"/>
             <xs:element ref="security:referrer-policy"/>
             <xs:element ref="security:feature-policy"/>
             <xs:element ref="security:feature-policy"/>
             <xs:element ref="security:permissions-policy"/>
             <xs:element ref="security:permissions-policy"/>
+            <xs:element ref="security:cross-origin-opener-policy"/>
+            <xs:element ref="security:cross-origin-embedder-policy"/>
+            <xs:element ref="security:cross-origin-resource-policy"/>
             <xs:element ref="security:header"/>
             <xs:element ref="security:header"/>
          </xs:choice>
          </xs:choice>
          <xs:attributeGroup ref="security:headers-options.attlist"/>
          <xs:attributeGroup ref="security:headers-options.attlist"/>
@@ -3151,6 +3154,77 @@
          </xs:annotation>
          </xs:annotation>
       </xs:attribute>
       </xs:attribute>
   </xs:attributeGroup>
   </xs:attributeGroup>
+  <xs:element name="cross-origin-opener-policy">
+      <xs:annotation>
+         <xs:documentation>Adds support for Cross-Origin-Opener-Policy header
+                </xs:documentation>
+      </xs:annotation>
+      <xs:complexType>
+         <xs:attributeGroup ref="security:cross-origin-opener-policy-options.attlist"/>
+      </xs:complexType>
+   </xs:element>
+  <xs:attributeGroup name="cross-origin-opener-policy-options.attlist">
+      <xs:attribute name="policy">
+         <xs:annotation>
+            <xs:documentation>The policies for the Cross-Origin-Opener-Policy header.
+                </xs:documentation>
+         </xs:annotation>
+         <xs:simpleType>
+            <xs:restriction base="xs:token">
+               <xs:enumeration value="unsafe-none"/>
+               <xs:enumeration value="same-origin"/>
+               <xs:enumeration value="same-origin-allow-popups"/>
+            </xs:restriction>
+         </xs:simpleType>
+      </xs:attribute>
+  </xs:attributeGroup>
+  <xs:element name="cross-origin-embedder-policy">
+      <xs:annotation>
+         <xs:documentation>Adds support for Cross-Origin-Embedder-Policy header
+                </xs:documentation>
+      </xs:annotation>
+      <xs:complexType>
+         <xs:attributeGroup ref="security:cross-origin-embedder-policy-options.attlist"/>
+      </xs:complexType>
+   </xs:element>
+  <xs:attributeGroup name="cross-origin-embedder-policy-options.attlist">
+      <xs:attribute name="policy">
+         <xs:annotation>
+            <xs:documentation>The policies for the Cross-Origin-Embedder-Policy header.
+                </xs:documentation>
+         </xs:annotation>
+         <xs:simpleType>
+            <xs:restriction base="xs:token">
+               <xs:enumeration value="unsafe-none"/>
+               <xs:enumeration value="require-corp"/>
+            </xs:restriction>
+         </xs:simpleType>
+      </xs:attribute>
+  </xs:attributeGroup>
+  <xs:element name="cross-origin-resource-policy">
+      <xs:annotation>
+         <xs:documentation>Adds support for Cross-Origin-Resource-Policy header
+                </xs:documentation>
+      </xs:annotation>
+      <xs:complexType>
+         <xs:attributeGroup ref="security:cross-origin-resource-policy-options.attlist"/>
+      </xs:complexType>
+   </xs:element>
+  <xs:attributeGroup name="cross-origin-resource-policy-options.attlist">
+      <xs:attribute name="policy">
+         <xs:annotation>
+            <xs:documentation>The policies for the Cross-Origin-Resource-Policy header.
+                </xs:documentation>
+         </xs:annotation>
+         <xs:simpleType>
+            <xs:restriction base="xs:token">
+               <xs:enumeration value="cross-origin"/>
+               <xs:enumeration value="same-origin"/>
+               <xs:enumeration value="same-site"/>
+            </xs:restriction>
+         </xs:simpleType>
+      </xs:attribute>
+  </xs:attributeGroup>
   <xs:element name="header">
   <xs:element name="header">
       <xs:annotation>
       <xs:annotation>
          <xs:documentation>Add additional headers to the response.
          <xs:documentation>Add additional headers to the response.

+ 77 - 1
config/src/test/java/org/springframework/security/config/annotation/web/configurers/HeadersConfigurerTests.java

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2021 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.
@@ -26,11 +26,16 @@ import org.junit.jupiter.api.extension.ExtendWith;
 
 
 import org.springframework.beans.factory.BeanCreationException;
 import org.springframework.beans.factory.BeanCreationException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
 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.config.test.SpringTestContext;
 import org.springframework.security.config.test.SpringTestContext;
 import org.springframework.security.config.test.SpringTestContextExtension;
 import org.springframework.security.config.test.SpringTestContextExtension;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.header.writers.CrossOriginEmbedderPolicyHeaderWriter;
+import org.springframework.security.web.header.writers.CrossOriginOpenerPolicyHeaderWriter;
+import org.springframework.security.web.header.writers.CrossOriginResourcePolicyHeaderWriter;
 import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy;
 import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy;
 import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode;
 import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.MockMvc;
@@ -52,6 +57,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
  * @author Eddú Meléndez
  * @author Eddú Meléndez
  * @author Vedran Pavic
  * @author Vedran Pavic
  * @author Eleftheria Stein
  * @author Eleftheria Stein
+ * @author Marcus Da Coregio
  */
  */
 @ExtendWith(SpringTestContextExtension.class)
 @ExtendWith(SpringTestContextExtension.class)
 public class HeadersConfigurerTests {
 public class HeadersConfigurerTests {
@@ -514,6 +520,30 @@ public class HeadersConfigurerTests {
 		assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.STRICT_TRANSPORT_SECURITY);
 		assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.STRICT_TRANSPORT_SECURITY);
 	}
 	}
 
 
+	@Test
+	public void getWhenCustomCrossOriginPoliciesInLambdaThenCrossOriginPolicyHeadersWithCustomValuesInResponse()
+			throws Exception {
+		this.spring.register(CrossOriginCustomPoliciesInLambdaConfig.class).autowire();
+		MvcResult mvcResult = this.mvc.perform(get("/"))
+				.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_OPENER_POLICY, "same-origin"))
+				.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_EMBEDDER_POLICY, "require-corp"))
+				.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_RESOURCE_POLICY, "same-origin")).andReturn();
+		assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.CROSS_ORIGIN_OPENER_POLICY,
+				HttpHeaders.CROSS_ORIGIN_EMBEDDER_POLICY, HttpHeaders.CROSS_ORIGIN_RESOURCE_POLICY);
+	}
+
+	@Test
+	public void getWhenCustomCrossOriginPoliciesThenCrossOriginPolicyHeadersWithCustomValuesInResponse()
+			throws Exception {
+		this.spring.register(CrossOriginCustomPoliciesConfig.class).autowire();
+		MvcResult mvcResult = this.mvc.perform(get("/"))
+				.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_OPENER_POLICY, "same-origin"))
+				.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_EMBEDDER_POLICY, "require-corp"))
+				.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_RESOURCE_POLICY, "same-origin")).andReturn();
+		assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.CROSS_ORIGIN_OPENER_POLICY,
+				HttpHeaders.CROSS_ORIGIN_EMBEDDER_POLICY, HttpHeaders.CROSS_ORIGIN_RESOURCE_POLICY);
+	}
+
 	@EnableWebSecurity
 	@EnableWebSecurity
 	static class HeadersConfig extends WebSecurityConfigurerAdapter {
 	static class HeadersConfig extends WebSecurityConfigurerAdapter {
 
 
@@ -1146,4 +1176,50 @@ public class HeadersConfigurerTests {
 
 
 	}
 	}
 
 
+	@EnableWebSecurity
+	static class CrossOriginCustomPoliciesInLambdaConfig {
+
+		@Bean
+		SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http.headers((headers) -> headers
+					.defaultsDisabled()
+					.crossOriginOpenerPolicy((policy) -> policy
+						.policy(CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy.SAME_ORIGIN)
+					)
+					.crossOriginEmbedderPolicy((policy) -> policy
+						.policy(CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP)
+					)
+					.crossOriginResourcePolicy((policy) -> policy
+						.policy(CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy.SAME_ORIGIN)
+					)
+			);
+			// @formatter:on
+			return http.build();
+		}
+
+	}
+
+	@EnableWebSecurity
+	static class CrossOriginCustomPoliciesConfig {
+
+		@Bean
+		SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http.headers()
+					.defaultsDisabled()
+					.crossOriginOpenerPolicy()
+						.policy(CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy.SAME_ORIGIN)
+						.and()
+					.crossOriginEmbedderPolicy()
+						.policy(CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP)
+						.and()
+					.crossOriginResourcePolicy()
+						.policy(CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy.SAME_ORIGIN);
+			// @formatter:on
+			return http.build();
+		}
+
+	}
+
 }
 }

+ 49 - 1
config/src/test/java/org/springframework/security/config/http/HttpHeadersConfigTests.java

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2021 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.
@@ -48,6 +48,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
  * @author Tim Ysewyn
  * @author Tim Ysewyn
  * @author Josh Cummings
  * @author Josh Cummings
  * @author Rafiullah Hamedy
  * @author Rafiullah Hamedy
+ * @author Marcus Da Coregio
  */
  */
 @ExtendWith(SpringTestContextExtension.class)
 @ExtendWith(SpringTestContextExtension.class)
 public class HttpHeadersConfigTests {
 public class HttpHeadersConfigTests {
@@ -733,6 +734,53 @@ public class HttpHeadersConfigTests {
 		// @formatter:on
 		// @formatter:on
 	}
 	}
 
 
+	@Test
+	public void requestWhenCrossOriginOpenerPolicyWithSameOriginAllowPopupsThenRespondsWithSameOriginAllowPopups()
+			throws Exception {
+		this.spring.configLocations(this.xml("DefaultsDisabledWithCrossOriginOpenerPolicy")).autowire();
+		// @formatter:off
+		this.mvc.perform(get("/"))
+				.andExpect(status().isOk())
+				.andExpect(excludesDefaults())
+				.andExpect(header().string("Cross-Origin-Opener-Policy", "same-origin-allow-popups"));
+		// @formatter:on
+	}
+
+	@Test
+	public void requestWhenCrossOriginEmbedderPolicyWithRequireCorpThenRespondsWithRequireCorp() throws Exception {
+		this.spring.configLocations(this.xml("DefaultsDisabledWithCrossOriginEmbedderPolicy")).autowire();
+		// @formatter:off
+		this.mvc.perform(get("/"))
+				.andExpect(status().isOk())
+				.andExpect(excludesDefaults())
+				.andExpect(header().string("Cross-Origin-Embedder-Policy", "require-corp"));
+		// @formatter:on
+	}
+
+	@Test
+	public void requestWhenCrossOriginResourcePolicyWithSameOriginThenRespondsWithSameOrigin() throws Exception {
+		this.spring.configLocations(this.xml("DefaultsDisabledWithCrossOriginResourcePolicy")).autowire();
+		// @formatter:off
+		this.mvc.perform(get("/"))
+				.andExpect(status().isOk())
+				.andExpect(excludesDefaults())
+				.andExpect(header().string("Cross-Origin-Resource-Policy", "same-origin"));
+		// @formatter:on
+	}
+
+	@Test
+	public void requestWhenCrossOriginPoliciesRespondsCrossOriginPolicies() throws Exception {
+		this.spring.configLocations(this.xml("DefaultsDisabledWithCrossOriginPolicies")).autowire();
+		// @formatter:off
+		this.mvc.perform(get("/"))
+				.andExpect(status().isOk())
+				.andExpect(excludesDefaults())
+				.andExpect(header().string("Cross-Origin-Opener-Policy", "same-origin"))
+				.andExpect(header().string("Cross-Origin-Embedder-Policy", "require-corp"))
+				.andExpect(header().string("Cross-Origin-Resource-Policy", "same-origin"));
+		// @formatter:on
+	}
+
 	private static ResultMatcher includesDefaults() {
 	private static ResultMatcher includesDefaults() {
 		return includes(defaultHeaders);
 		return includes(defaultHeaders);
 	}
 	}

+ 52 - 1
config/src/test/java/org/springframework/security/config/web/server/HeaderSpecTests.java

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2021 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.
@@ -30,6 +30,9 @@ import org.springframework.http.HttpHeaders;
 import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
 import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
 import org.springframework.security.web.server.header.ContentSecurityPolicyServerHttpHeadersWriter;
 import org.springframework.security.web.server.header.ContentSecurityPolicyServerHttpHeadersWriter;
 import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter;
 import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter;
+import org.springframework.security.web.server.header.CrossOriginEmbedderPolicyServerHttpHeadersWriter;
+import org.springframework.security.web.server.header.CrossOriginOpenerPolicyServerHttpHeadersWriter;
+import org.springframework.security.web.server.header.CrossOriginResourcePolicyServerHttpHeadersWriter;
 import org.springframework.security.web.server.header.FeaturePolicyServerHttpHeadersWriter;
 import org.springframework.security.web.server.header.FeaturePolicyServerHttpHeadersWriter;
 import org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter;
 import org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter;
 import org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter.ReferrerPolicy;
 import org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter.ReferrerPolicy;
@@ -48,6 +51,7 @@ import static org.springframework.security.config.Customizer.withDefaults;
  * @author Rob Winch
  * @author Rob Winch
  * @author Vedran Pavic
  * @author Vedran Pavic
  * @author Ankur Pathak
  * @author Ankur Pathak
+ * @author Marcus Da Coregio
  * @since 5.0
  * @since 5.0
  */
  */
 public class HeaderSpecTests {
 public class HeaderSpecTests {
@@ -406,6 +410,53 @@ public class HeaderSpecTests {
 		assertHeaders();
 		assertHeaders();
 	}
 	}
 
 
+	@Test
+	public void headersWhenCrossOriginPoliciesCustomEnabledThenCustomCrossOriginPoliciesWritten() {
+		this.expectedHeaders.add(CrossOriginOpenerPolicyServerHttpHeadersWriter.OPENER_POLICY,
+				CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS
+						.getPolicy());
+		this.expectedHeaders.add(CrossOriginEmbedderPolicyServerHttpHeadersWriter.EMBEDDER_POLICY,
+				CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP.getPolicy());
+		this.expectedHeaders.add(CrossOriginResourcePolicyServerHttpHeadersWriter.RESOURCE_POLICY,
+				CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy.SAME_ORIGIN.getPolicy());
+		// @formatter:off
+		this.http.headers()
+				.crossOriginOpenerPolicy()
+						.policy(CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS)
+						.and()
+				.crossOriginEmbedderPolicy()
+						.policy(CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP)
+						.and()
+				.crossOriginResourcePolicy()
+						.policy(CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy.SAME_ORIGIN);
+		// @formatter:on
+		assertHeaders();
+	}
+
+	@Test
+	public void headersWhenCrossOriginPoliciesCustomEnabledInLambdaThenCustomCrossOriginPoliciesWritten() {
+		this.expectedHeaders.add(CrossOriginOpenerPolicyServerHttpHeadersWriter.OPENER_POLICY,
+				CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS
+						.getPolicy());
+		this.expectedHeaders.add(CrossOriginEmbedderPolicyServerHttpHeadersWriter.EMBEDDER_POLICY,
+				CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP.getPolicy());
+		this.expectedHeaders.add(CrossOriginResourcePolicyServerHttpHeadersWriter.RESOURCE_POLICY,
+				CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy.SAME_ORIGIN.getPolicy());
+		// @formatter:off
+		this.http.headers()
+				.crossOriginOpenerPolicy((policy) -> policy
+						.policy(CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS)
+				)
+				.crossOriginEmbedderPolicy((policy) -> policy
+						.policy(CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP)
+				)
+				.crossOriginResourcePolicy((policy) -> policy
+						.policy(CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy.SAME_ORIGIN)
+				);
+		// @formatter:on
+		assertHeaders();
+	}
+
 	private void expectHeaderNamesNotPresent(String... headerNames) {
 	private void expectHeaderNamesNotPresent(String... headerNames) {
 		for (String headerName : headerNames) {
 		for (String headerName : headerNames) {
 			this.expectedHeaders.remove(headerName);
 			this.expectedHeaders.remove(headerName);

+ 59 - 0
config/src/test/kotlin/org/springframework/security/config/web/server/ServerHeadersDslTests.kt

@@ -28,6 +28,9 @@ import org.springframework.security.config.test.SpringTestContextExtension
 import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter
 import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter
 import org.springframework.security.web.server.SecurityWebFilterChain
 import org.springframework.security.web.server.SecurityWebFilterChain
 import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter
 import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter
+import org.springframework.security.web.server.header.CrossOriginEmbedderPolicyServerHttpHeadersWriter
+import org.springframework.security.web.server.header.CrossOriginOpenerPolicyServerHttpHeadersWriter
+import org.springframework.security.web.server.header.CrossOriginResourcePolicyServerHttpHeadersWriter
 import org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter
 import org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter
 import org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter
 import org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter
 import org.springframework.security.web.server.header.XXssProtectionServerHttpHeadersWriter
 import org.springframework.security.web.server.header.XXssProtectionServerHttpHeadersWriter
@@ -133,4 +136,60 @@ class ServerHeadersDslTests {
             }
             }
         }
         }
     }
     }
+
+    @Test
+    fun `request when no cross-origin policies configured then does not write cross-origin policies headers in response`() {
+        this.spring.register(CrossOriginPoliciesConfig::class.java).autowire()
+
+        this.client.get()
+                .uri("/")
+                .exchange()
+                .expectHeader().doesNotExist("Cross-Origin-Opener-Policy")
+                .expectHeader().doesNotExist("Cross-Origin-Embedder-Policy")
+                .expectHeader().doesNotExist("Cross-Origin-Resource-Policy")
+    }
+
+    @EnableWebFluxSecurity
+    @EnableWebFlux
+    open class CrossOriginPoliciesConfig {
+        @Bean
+        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
+            return http {
+                headers { }
+            }
+        }
+    }
+
+    @Test
+    fun `request when cross-origin custom policies configured then cross-origin custom policies headers in response`() {
+        this.spring.register(CrossOriginPoliciesCustomConfig::class.java).autowire()
+
+        this.client.get()
+                .uri("/")
+                .exchange()
+                .expectHeader().valueEquals("Cross-Origin-Opener-Policy", "same-origin")
+                .expectHeader().valueEquals("Cross-Origin-Embedder-Policy", "require-corp")
+                .expectHeader().valueEquals("Cross-Origin-Resource-Policy", "same-origin")
+    }
+
+    @EnableWebFluxSecurity
+    @EnableWebFlux
+    open class CrossOriginPoliciesCustomConfig {
+        @Bean
+        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
+            return http {
+                headers {
+                    crossOriginOpenerPolicy {
+                        policy = CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy.SAME_ORIGIN
+                    }
+                    crossOriginEmbedderPolicy {
+                        policy = CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP
+                    }
+                    crossOriginResourcePolicy {
+                        policy = CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy.SAME_ORIGIN
+                    }
+                }
+            }
+        }
+    }
 }
 }

+ 1 - 1
config/src/test/kotlin/org/springframework/security/config/web/servlet/HeadersDslTests.kt

@@ -19,13 +19,13 @@ package org.springframework.security.config.web.servlet
 import org.junit.jupiter.api.Test
 import org.junit.jupiter.api.Test
 import org.junit.jupiter.api.extension.ExtendWith
 import org.junit.jupiter.api.extension.ExtendWith
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.context.annotation.Bean
 import org.springframework.http.HttpHeaders
 import org.springframework.http.HttpHeaders
 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.config.test.SpringTestContext
 import org.springframework.security.config.test.SpringTestContext
 import org.springframework.security.config.test.SpringTestContextExtension
 import org.springframework.security.config.test.SpringTestContextExtension
-import org.springframework.security.config.web.servlet.headers.PermissionsPolicyDsl
 import org.springframework.security.web.header.writers.StaticHeadersWriter
 import org.springframework.security.web.header.writers.StaticHeadersWriter
 import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter
 import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter
 import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter
 import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter

+ 36 - 0
config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithCrossOriginEmbedderPolicy.xml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2021 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
+  ~
+  ~      https://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.
+  -->
+
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		xmlns="http://www.springframework.org/schema/security"
+		xsi:schemaLocation="
+			http://www.springframework.org/schema/security
+			https://www.springframework.org/schema/security/spring-security.xsd
+			http://www.springframework.org/schema/beans
+			https://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<http auto-config="true">
+		<headers defaults-disabled="true">
+			<cross-origin-embedder-policy policy="require-corp"/>
+		</headers>
+	</http>
+
+	<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
+
+	<b:import resource="userservice.xml"/>
+</b:beans>

+ 36 - 0
config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithCrossOriginOpenerPolicy.xml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2021 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
+  ~
+  ~      https://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.
+  -->
+
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		xmlns="http://www.springframework.org/schema/security"
+		xsi:schemaLocation="
+			http://www.springframework.org/schema/security
+			https://www.springframework.org/schema/security/spring-security.xsd
+			http://www.springframework.org/schema/beans
+			https://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<http auto-config="true">
+		<headers defaults-disabled="true">
+			<cross-origin-opener-policy policy="same-origin-allow-popups"/>
+		</headers>
+	</http>
+
+	<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
+
+	<b:import resource="userservice.xml"/>
+</b:beans>

+ 38 - 0
config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithCrossOriginPolicies.xml

@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2021 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
+  ~
+  ~      https://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.
+  -->
+
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		xmlns="http://www.springframework.org/schema/security"
+		xsi:schemaLocation="
+			http://www.springframework.org/schema/security
+			https://www.springframework.org/schema/security/spring-security.xsd
+			http://www.springframework.org/schema/beans
+			https://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<http auto-config="true">
+		<headers defaults-disabled="true">
+			<cross-origin-opener-policy policy="same-origin"/>
+			<cross-origin-embedder-policy policy="require-corp"/>
+			<cross-origin-resource-policy policy="same-origin"/>
+		</headers>
+	</http>
+
+	<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
+
+	<b:import resource="userservice.xml"/>
+</b:beans>

+ 36 - 0
config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithCrossOriginResourcePolicy.xml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2021 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
+  ~
+  ~      https://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.
+  -->
+
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		xmlns="http://www.springframework.org/schema/security"
+		xsi:schemaLocation="
+			http://www.springframework.org/schema/security
+			https://www.springframework.org/schema/security/spring-security.xsd
+			http://www.springframework.org/schema/beans
+			https://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<http auto-config="true">
+		<headers defaults-disabled="true">
+			<cross-origin-resource-policy policy="same-origin"/>
+		</headers>
+	</http>
+
+	<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
+
+	<b:import resource="userservice.xml"/>
+</b:beans>

+ 20 - 0
docs/modules/ROOT/pages/features/exploits/headers.adoc

@@ -378,6 +378,26 @@ Clear-Site-Data: "cache", "cookies", "storage", "executionContexts"
 
 
 This is a nice clean-up action to perform on logout.
 This is a nice clean-up action to perform on logout.
 
 
+[[headers-cross-origin-policies]]
+== Cross-Origin Policies
+
+[NOTE]
+====
+Refer to the relevant sections to see how to configure for both <<servlet-headers-cross-origin-policies,servlet>> and <<webflux-headers-cross-origin-policies,webflux>> based applications.
+====
+
+Spring Security provides support for some important Cross-Origin Policies headers.
+Those headers are:
+
+* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy[`Cross-Origin-Opener-Policy`]
+* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy[`Cross-Origin-Embedder-Policy`]
+* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy[`Cross-Origin-Resource-Policy`]
+
+`Cross-Origin-Opener-Policy` (COOP) allows a top-level document to break the association between its window and any others in the browsing context group (e.g., between a popup and its opener), preventing any direct DOM access between them.
+
+Enabling `Cross-Origin-Embedder-Policy` (COEP) prevents a document from loading any non-same-origin resources which don't explicitly grant the document permission to be loaded.
+
+The `Cross-Origin-Resource-Policy` (CORP) header allows you to control the set of origins that are empowered to include a resource. It is a robust defense against attacks like https://meltdownattack.com[Spectre], as it allows browsers to block a given response before it enters an attacker's process.
 
 
 [[headers-custom]]
 [[headers-custom]]
 == Custom Headers
 == Custom Headers

+ 62 - 0
docs/modules/ROOT/pages/reactive/exploits/headers.adoc

@@ -578,3 +578,65 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
 }
 }
 ----
 ----
 ====
 ====
+
+[[webflux-headers-cross-origin-policies]]
+== Cross-Origin Policies
+
+Spring Security provides built-in support for adding some Cross-Origin policies headers, those headers are:
+
+[source]
+----
+Cross-Origin-Opener-Policy
+Cross-Origin-Embedder-Policy
+Cross-Origin-Resource-Policy
+----
+
+Spring Security does not add <<headers-cross-origin-policies,Cross-Origin Policies>> headers by default.
+The headers can be added with the following configuration:
+
+.Cross-Origin Policies
+====
+.Java
+[source,java,role="primary"]
+----
+@EnableWebFluxSecurity
+@EnableWebFlux
+public class WebSecurityConfig {
+
+    @Bean
+    SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http) {
+        http.headers((headers) -> headers
+                .crossOriginOpenerPolicy(CrossOriginOpenerPolicy.SAME_ORIGIN)
+                .crossOriginEmbedderPolicy(CrossOriginEmbedderPolicy.REQUIRE_CORP)
+                .crossOriginResourcePolicy(CrossOriginResourcePolicy.SAME_ORIGIN));
+        return http.build();
+    }
+}
+----
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@EnableWebFluxSecurity
+@EnableWebFlux
+open class CrossOriginPoliciesCustomConfig {
+    @Bean
+    open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
+        return http {
+            headers {
+                crossOriginOpenerPolicy(CrossOriginOpenerPolicy.SAME_ORIGIN)
+                crossOriginEmbedderPolicy(CrossOriginEmbedderPolicy.REQUIRE_CORP)
+                crossOriginResourcePolicy(CrossOriginResourcePolicy.SAME_ORIGIN)
+            }
+        }
+    }
+}
+----
+====
+
+This configuration will write the headers with the values provided:
+[source]
+----
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Resource-Policy: same-origin
+----

+ 66 - 0
docs/modules/ROOT/pages/servlet/appendix/namespace/http.adoc

@@ -238,6 +238,9 @@ This allows HTTPS websites to resist impersonation by attackers using mis-issued
 https://www.w3.org/TR/CSP2/[Content Security Policy (CSP)] is a mechanism that web applications can leverage to mitigate content injection vulnerabilities, such as cross-site scripting (XSS).
 https://www.w3.org/TR/CSP2/[Content Security Policy (CSP)] is a mechanism that web applications can leverage to mitigate content injection vulnerabilities, such as cross-site scripting (XSS).
 ** `Referrer-Policy` - Can be set using the <<nsa-referrer-policy,referrer-policy>> element, https://www.w3.org/TR/referrer-policy/[Referrer-Policy] is a mechanism that web applications can leverage to manage the referrer field, which contains the last page the user was on.
 ** `Referrer-Policy` - Can be set using the <<nsa-referrer-policy,referrer-policy>> element, https://www.w3.org/TR/referrer-policy/[Referrer-Policy] is a mechanism that web applications can leverage to manage the referrer field, which contains the last page the user was on.
 ** `Feature-Policy` - Can be set using the <<nsa-feature-policy,feature-policy>> element, https://wicg.github.io/feature-policy/[Feature-Policy] is a mechanism that allows web developers to selectively enable, disable, and modify the behavior of certain APIs and web features in the browser.
 ** `Feature-Policy` - Can be set using the <<nsa-feature-policy,feature-policy>> element, https://wicg.github.io/feature-policy/[Feature-Policy] is a mechanism that allows web developers to selectively enable, disable, and modify the behavior of certain APIs and web features in the browser.
+** `Cross-Origin-Opener-Policy` - Can be set using the <<nsa-cross-origin-opener-policy,cross-origin-opener-policy>> element, https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy[Cross-Origin-Opener-Policy] is a mechanism that allows you to ensure a top-level document does not share a browsing context group with cross-origin documents.
+** `Cross-Origin-Embedder-Policy` - Can be set using the <<nsa-cross-origin-embedder-policy,cross-origin-embedder-policy>> element, https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy[Cross-Origin-Embedder-Policy] is a mechanism that prevents a document from loading any cross-origin resources that don't explicitly grant the document permission.
+** `Cross-Origin-Resource-Policy` - Can be set using the <<nsa-cross-origin-resource-policy,cross-origin-resource-policy>> element, https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy[Cross-Origin-Resource-Policy] is a mechanism that conveys a desire that the browser blocks no-cors cross-origin/cross-site requests to the given resource.
 
 
 [[nsa-headers-attributes]]
 [[nsa-headers-attributes]]
 === <headers> Attributes
 === <headers> Attributes
@@ -269,6 +272,9 @@ The default is false (the headers are enabled).
 * <<nsa-cache-control,cache-control>>
 * <<nsa-cache-control,cache-control>>
 * <<nsa-content-security-policy,content-security-policy>>
 * <<nsa-content-security-policy,content-security-policy>>
 * <<nsa-content-type-options,content-type-options>>
 * <<nsa-content-type-options,content-type-options>>
+* <<nsa-cross-origin-embedder-policy,cross-origin-embedder-policy>>
+* <<nsa-cross-origin-opener-policy,cross-origin-opener-policy>>
+* <<nsa-cross-origin-resource-policy,cross-origin-resource-policy>>
 * <<nsa-feature-policy,feature-policy>>
 * <<nsa-feature-policy,feature-policy>>
 * <<nsa-frame-options,frame-options>>
 * <<nsa-frame-options,frame-options>>
 * <<nsa-header,header>>
 * <<nsa-header,header>>
@@ -584,6 +590,66 @@ Default false.
 
 
 
 
 
 
+[[nsa-cross-origin-embedder-policy]]
+==== <cross-origin-embedder-policy>
+When enabled adds the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy[Cross-Origin-Embedder-Policy] header to the response.
+
+
+[[nsa-cross-origin-embedder-policy-attributes]]
+===== <cross-origin-embedder-policy> Attributes
+
+[[nsa-cross-origin-embedder-policy-policy]]
+* **policy**
+The policy for the `Cross-Origin-Embedder-Policy` header.
+
+[[nsa-cross-origin-embedder-policy-parents]]
+===== Parent Elements of <cross-origin-embedder-policy>
+
+
+* <<nsa-headers,headers>>
+
+
+
+[[nsa-cross-origin-opener-policy]]
+==== <cross-origin-opener-policy>
+When enabled adds the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy[Cross-Origin-Opener-Policy] header to the response.
+
+
+[[nsa-cross-origin-opener-policy-attributes]]
+===== <cross-origin-opener-policy> Attributes
+
+[[nsa-cross-origin-opener-policy-policy]]
+* **policy**
+The policy for the `Cross-Origin-Opener-Policy` header.
+
+[[nsa-cross-origin-opener-policy-parents]]
+===== Parent Elements of <cross-origin-opener-policy>
+
+
+* <<nsa-headers,headers>>
+
+
+
+[[nsa-cross-origin-resource-policy]]
+==== <cross-origin-resource-policy>
+When enabled adds the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy[Cross-Origin-Resource-Policy] header to the response.
+
+
+[[nsa-cross-origin-resource-policy-attributes]]
+===== <cross-origin-resource-policy> Attributes
+
+[[nsa-cross-origin-resource-policy-policy]]
+* **policy**
+The policy for the `Cross-Origin-Resource-Policy` header.
+
+[[nsa-cross-origin-resource-policy-parents]]
+===== Parent Elements of <cross-origin-resource-policy>
+
+
+* <<nsa-headers,headers>>
+
+
+
 [[nsa-header]]
 [[nsa-header]]
 == <header>
 == <header>
 Add additional headers to the response, both the name and value need to be specified.
 Add additional headers to the response, both the name and value need to be specified.

+ 61 - 0
docs/modules/ROOT/pages/servlet/exploits/headers.adoc

@@ -938,6 +938,67 @@ class SecurityConfig : WebSecurityConfigurerAdapter() {
 ----
 ----
 ====
 ====
 
 
+[[servlet-headers-cross-origin-policies]]
+== Cross-Origin Policies
+
+Spring Security provides built-in support for adding some Cross-Origin policies headers, those headers are:
+
+[source]
+----
+Cross-Origin-Opener-Policy
+Cross-Origin-Embedder-Policy
+Cross-Origin-Resource-Policy
+----
+
+Spring Security does not add <<headers-cross-origin-policies,Cross-Origin Policies>> headers by default.
+The headers can be added with the following configuration:
+
+.Cross-Origin Policies
+====
+.Java
+[source,java,role="primary"]
+----
+@EnableWebSecurity
+public class WebSecurityConfig {
+
+    @Bean
+    SecurityFilterChain securityFilterChain(HttpSecurity http) {
+        http.headers((headers) -> headers
+                .crossOriginOpenerPolicy(CrossOriginOpenerPolicy.SAME_ORIGIN)
+                .crossOriginEmbedderPolicy(CrossOriginEmbedderPolicy.REQUIRE_CORP)
+                .crossOriginResourcePolicy(CrossOriginResourcePolicy.SAME_ORIGIN)));
+        return http.build();
+    }
+}
+----
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@EnableWebSecurity
+open class CrossOriginPoliciesConfig {
+    @Bean
+    open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
+        http {
+            headers {
+                crossOriginOpenerPolicy(CrossOriginOpenerPolicy.SAME_ORIGIN)
+                crossOriginEmbedderPolicy(CrossOriginEmbedderPolicy.REQUIRE_CORP)
+                crossOriginResourcePolicy(CrossOriginResourcePolicy.SAME_ORIGIN)
+            }
+        }
+        return http.build()
+    }
+}
+----
+====
+
+This configuration will write the headers with the values provided:
+[source]
+----
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Resource-Policy: same-origin
+----
+
 [[servlet-headers-custom]]
 [[servlet-headers-custom]]
 == Custom Headers
 == Custom Headers
 Spring Security has mechanisms to make it convenient to add the more common security headers to your application.
 Spring Security has mechanisms to make it convenient to add the more common security headers to your application.

+ 84 - 0
web/src/main/java/org/springframework/security/web/header/writers/CrossOriginEmbedderPolicyHeaderWriter.java

@@ -0,0 +1,84 @@
+/*
+ * Copyright 2002-2021 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
+ *
+ *      https://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.header.writers;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.web.header.HeaderWriter;
+import org.springframework.util.Assert;
+
+/**
+ * Inserts Cross-Origin-Embedder-Policy header.
+ *
+ * @author Marcus Da Coregio
+ * @since 5.7
+ * @see <a href=
+ * "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
+ * Cross-Origin-Embedder-Policy</a>
+ */
+public final class CrossOriginEmbedderPolicyHeaderWriter implements HeaderWriter {
+
+	private static final String EMBEDDER_POLICY = "Cross-Origin-Embedder-Policy";
+
+	private CrossOriginEmbedderPolicy policy;
+
+	/**
+	 * Sets the {@link CrossOriginEmbedderPolicy} value to be used in the
+	 * {@code Cross-Origin-Embedder-Policy} header
+	 * @param embedderPolicy the {@link CrossOriginEmbedderPolicy} to use
+	 */
+	public void setPolicy(CrossOriginEmbedderPolicy embedderPolicy) {
+		Assert.notNull(embedderPolicy, "embedderPolicy cannot be null");
+		this.policy = embedderPolicy;
+	}
+
+	@Override
+	public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
+		if (this.policy != null && !response.containsHeader(EMBEDDER_POLICY)) {
+			response.addHeader(EMBEDDER_POLICY, this.policy.getPolicy());
+		}
+	}
+
+	public enum CrossOriginEmbedderPolicy {
+
+		UNSAFE_NONE("unsafe-none"),
+
+		REQUIRE_CORP("require-corp");
+
+		private final String policy;
+
+		CrossOriginEmbedderPolicy(String policy) {
+			this.policy = policy;
+		}
+
+		public String getPolicy() {
+			return this.policy;
+		}
+
+		public static CrossOriginEmbedderPolicy from(String embedderPolicy) {
+			for (CrossOriginEmbedderPolicy policy : values()) {
+				if (policy.getPolicy().equals(embedderPolicy)) {
+					return policy;
+				}
+			}
+			return null;
+		}
+
+	}
+
+}

+ 86 - 0
web/src/main/java/org/springframework/security/web/header/writers/CrossOriginOpenerPolicyHeaderWriter.java

@@ -0,0 +1,86 @@
+/*
+ * Copyright 2002-2021 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
+ *
+ *      https://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.header.writers;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.web.header.HeaderWriter;
+import org.springframework.util.Assert;
+
+/**
+ * Inserts the Cross-Origin-Opener-Policy header
+ *
+ * @author Marcus Da Coregio
+ * @since 5.7
+ * @see <a href=
+ * "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
+ * Cross-Origin-Opener-Policy</a>
+ */
+public final class CrossOriginOpenerPolicyHeaderWriter implements HeaderWriter {
+
+	private static final String OPENER_POLICY = "Cross-Origin-Opener-Policy";
+
+	private CrossOriginOpenerPolicy policy;
+
+	/**
+	 * Sets the {@link CrossOriginOpenerPolicy} value to be used in the
+	 * {@code Cross-Origin-Opener-Policy} header
+	 * @param openerPolicy the {@link CrossOriginOpenerPolicy} to use
+	 */
+	public void setPolicy(CrossOriginOpenerPolicy openerPolicy) {
+		Assert.notNull(openerPolicy, "openerPolicy cannot be null");
+		this.policy = openerPolicy;
+	}
+
+	@Override
+	public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
+		if (this.policy != null && !response.containsHeader(OPENER_POLICY)) {
+			response.addHeader(OPENER_POLICY, this.policy.getPolicy());
+		}
+	}
+
+	public enum CrossOriginOpenerPolicy {
+
+		UNSAFE_NONE("unsafe-none"),
+
+		SAME_ORIGIN_ALLOW_POPUPS("same-origin-allow-popups"),
+
+		SAME_ORIGIN("same-origin");
+
+		private final String policy;
+
+		CrossOriginOpenerPolicy(String policy) {
+			this.policy = policy;
+		}
+
+		public String getPolicy() {
+			return this.policy;
+		}
+
+		public static CrossOriginOpenerPolicy from(String openerPolicy) {
+			for (CrossOriginOpenerPolicy policy : values()) {
+				if (policy.getPolicy().equals(openerPolicy)) {
+					return policy;
+				}
+			}
+			return null;
+		}
+
+	}
+
+}

+ 86 - 0
web/src/main/java/org/springframework/security/web/header/writers/CrossOriginResourcePolicyHeaderWriter.java

@@ -0,0 +1,86 @@
+/*
+ * Copyright 2002-2021 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
+ *
+ *      https://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.header.writers;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.web.header.HeaderWriter;
+import org.springframework.util.Assert;
+
+/**
+ * Inserts Cross-Origin-Resource-Policy header
+ *
+ * @author Marcus Da Coregio
+ * @since 5.7
+ * @see <a href=
+ * "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
+ * Cross-Origin-Resource-Policy</a>
+ */
+public final class CrossOriginResourcePolicyHeaderWriter implements HeaderWriter {
+
+	private static final String RESOURCE_POLICY = "Cross-Origin-Resource-Policy";
+
+	private CrossOriginResourcePolicy policy;
+
+	/**
+	 * Sets the {@link CrossOriginResourcePolicy} value to be used in the
+	 * {@code Cross-Origin-Resource-Policy} header
+	 * @param resourcePolicy the {@link CrossOriginResourcePolicy} to use
+	 */
+	public void setPolicy(CrossOriginResourcePolicy resourcePolicy) {
+		Assert.notNull(resourcePolicy, "resourcePolicy cannot be null");
+		this.policy = resourcePolicy;
+	}
+
+	@Override
+	public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
+		if (this.policy != null && !response.containsHeader(RESOURCE_POLICY)) {
+			response.addHeader(RESOURCE_POLICY, this.policy.getPolicy());
+		}
+	}
+
+	public enum CrossOriginResourcePolicy {
+
+		SAME_SITE("same-site"),
+
+		SAME_ORIGIN("same-origin"),
+
+		CROSS_ORIGIN("cross-origin");
+
+		private final String policy;
+
+		CrossOriginResourcePolicy(String policy) {
+			this.policy = policy;
+		}
+
+		public String getPolicy() {
+			return this.policy;
+		}
+
+		public static CrossOriginResourcePolicy from(String resourcePolicy) {
+			for (CrossOriginResourcePolicy policy : values()) {
+				if (policy.getPolicy().equals(resourcePolicy)) {
+					return policy;
+				}
+			}
+			return null;
+		}
+
+	}
+
+}

+ 78 - 0
web/src/main/java/org/springframework/security/web/server/header/CrossOriginEmbedderPolicyServerHttpHeadersWriter.java

@@ -0,0 +1,78 @@
+/*
+ * Copyright 2002-2021 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
+ *
+ *      https://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.server.header;
+
+import reactor.core.publisher.Mono;
+
+import org.springframework.util.Assert;
+import org.springframework.web.server.ServerWebExchange;
+
+/**
+ * Inserts Cross-Origin-Embedder-Policy headers.
+ *
+ * @author Marcus Da Coregio
+ * @since 5.7
+ * @see <a href=
+ * "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
+ * Cross-Origin-Embedder-Policy</a>
+ */
+public final class CrossOriginEmbedderPolicyServerHttpHeadersWriter implements ServerHttpHeadersWriter {
+
+	public static final String EMBEDDER_POLICY = "Cross-Origin-Embedder-Policy";
+
+	private ServerHttpHeadersWriter delegate;
+
+	/**
+	 * Sets the {@link CrossOriginEmbedderPolicy} value to be used in the
+	 * {@code Cross-Origin-Embedder-Policy} header
+	 * @param embedderPolicy the {@link CrossOriginEmbedderPolicy} to use
+	 */
+	public void setPolicy(CrossOriginEmbedderPolicy embedderPolicy) {
+		Assert.notNull(embedderPolicy, "embedderPolicy cannot be null");
+		this.delegate = createDelegate(embedderPolicy);
+	}
+
+	@Override
+	public Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {
+		return (this.delegate != null) ? this.delegate.writeHttpHeaders(exchange) : Mono.empty();
+	}
+
+	private static ServerHttpHeadersWriter createDelegate(CrossOriginEmbedderPolicy embedderPolicy) {
+		StaticServerHttpHeadersWriter.Builder builder = StaticServerHttpHeadersWriter.builder();
+		builder.header(EMBEDDER_POLICY, embedderPolicy.getPolicy());
+		return builder.build();
+	}
+
+	public enum CrossOriginEmbedderPolicy {
+
+		UNSAFE_NONE("unsafe-none"),
+
+		REQUIRE_CORP("require-corp");
+
+		private final String policy;
+
+		CrossOriginEmbedderPolicy(String policy) {
+			this.policy = policy;
+		}
+
+		public String getPolicy() {
+			return this.policy;
+		}
+
+	}
+
+}

+ 80 - 0
web/src/main/java/org/springframework/security/web/server/header/CrossOriginOpenerPolicyServerHttpHeadersWriter.java

@@ -0,0 +1,80 @@
+/*
+ * Copyright 2002-2021 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
+ *
+ *      https://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.server.header;
+
+import reactor.core.publisher.Mono;
+
+import org.springframework.util.Assert;
+import org.springframework.web.server.ServerWebExchange;
+
+/**
+ * Inserts Cross-Origin-Opener-Policy header.
+ *
+ * @author Marcus Da Coregio
+ * @since 5.7
+ * @see <a href=
+ * "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
+ * Cross-Origin-Opener-Policy</a>
+ */
+public final class CrossOriginOpenerPolicyServerHttpHeadersWriter implements ServerHttpHeadersWriter {
+
+	public static final String OPENER_POLICY = "Cross-Origin-Opener-Policy";
+
+	private ServerHttpHeadersWriter delegate;
+
+	/**
+	 * Sets the {@link CrossOriginOpenerPolicy} value to be used in the
+	 * {@code Cross-Origin-Opener-Policy} header
+	 * @param openerPolicy the {@link CrossOriginOpenerPolicy} to use
+	 */
+	public void setPolicy(CrossOriginOpenerPolicy openerPolicy) {
+		Assert.notNull(openerPolicy, "openerPolicy cannot be null");
+		this.delegate = createDelegate(openerPolicy);
+	}
+
+	@Override
+	public Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {
+		return (this.delegate != null) ? this.delegate.writeHttpHeaders(exchange) : Mono.empty();
+	}
+
+	private static ServerHttpHeadersWriter createDelegate(CrossOriginOpenerPolicy openerPolicy) {
+		StaticServerHttpHeadersWriter.Builder builder = StaticServerHttpHeadersWriter.builder();
+		builder.header(OPENER_POLICY, openerPolicy.getPolicy());
+		return builder.build();
+	}
+
+	public enum CrossOriginOpenerPolicy {
+
+		UNSAFE_NONE("unsafe-none"),
+
+		SAME_ORIGIN_ALLOW_POPUPS("same-origin-allow-popups"),
+
+		SAME_ORIGIN("same-origin");
+
+		private final String policy;
+
+		CrossOriginOpenerPolicy(String policy) {
+			this.policy = policy;
+		}
+
+		public String getPolicy() {
+			return this.policy;
+		}
+
+	}
+
+}

+ 80 - 0
web/src/main/java/org/springframework/security/web/server/header/CrossOriginResourcePolicyServerHttpHeadersWriter.java

@@ -0,0 +1,80 @@
+/*
+ * Copyright 2002-2021 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
+ *
+ *      https://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.server.header;
+
+import reactor.core.publisher.Mono;
+
+import org.springframework.util.Assert;
+import org.springframework.web.server.ServerWebExchange;
+
+/**
+ * Inserts Cross-Origin-Resource-Policy headers.
+ *
+ * @author Marcus Da Coregio
+ * @since 5.7
+ * @see <a href=
+ * "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
+ * Cross-Origin-Resource-Policy</a>
+ */
+public final class CrossOriginResourcePolicyServerHttpHeadersWriter implements ServerHttpHeadersWriter {
+
+	public static final String RESOURCE_POLICY = "Cross-Origin-Resource-Policy";
+
+	private ServerHttpHeadersWriter delegate;
+
+	/**
+	 * Sets the {@link CrossOriginResourcePolicy} value to be used in the
+	 * {@code Cross-Origin-Embedder-Policy} header
+	 * @param resourcePolicy the {@link CrossOriginResourcePolicy} to use
+	 */
+	public void setPolicy(CrossOriginResourcePolicy resourcePolicy) {
+		Assert.notNull(resourcePolicy, "resourcePolicy cannot be null");
+		this.delegate = createDelegate(resourcePolicy);
+	}
+
+	@Override
+	public Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {
+		return (this.delegate != null) ? this.delegate.writeHttpHeaders(exchange) : Mono.empty();
+	}
+
+	private static ServerHttpHeadersWriter createDelegate(CrossOriginResourcePolicy resourcePolicy) {
+		StaticServerHttpHeadersWriter.Builder builder = StaticServerHttpHeadersWriter.builder();
+		builder.header(RESOURCE_POLICY, resourcePolicy.getPolicy());
+		return builder.build();
+	}
+
+	public enum CrossOriginResourcePolicy {
+
+		SAME_SITE("same-site"),
+
+		SAME_ORIGIN("same-origin"),
+
+		CROSS_ORIGIN("cross-origin");
+
+		private final String policy;
+
+		CrossOriginResourcePolicy(String policy) {
+			this.policy = policy;
+		}
+
+		public String getPolicy() {
+			return this.policy;
+		}
+
+	}
+
+}

+ 80 - 0
web/src/test/java/org/springframework/security/web/header/writers/CrossOriginEmbedderPolicyHeaderWriterTests.java

@@ -0,0 +1,80 @@
+/*
+ * Copyright 2002-2021 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
+ *
+ *      https://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.header.writers;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+
+class CrossOriginEmbedderPolicyHeaderWriterTests {
+
+	private static final String EMBEDDER_HEADER_NAME = "Cross-Origin-Embedder-Policy";
+
+	private CrossOriginEmbedderPolicyHeaderWriter writer;
+
+	private MockHttpServletRequest request;
+
+	private MockHttpServletResponse response;
+
+	@BeforeEach
+	void setup() {
+		this.writer = new CrossOriginEmbedderPolicyHeaderWriter();
+		this.request = new MockHttpServletRequest();
+		this.response = new MockHttpServletResponse();
+	}
+
+	@Test
+	void setEmbedderPolicyWhenNullEmbedderPolicyThenThrowsIllegalArgument() {
+		assertThatIllegalArgumentException().isThrownBy(() -> this.writer.setPolicy(null))
+				.withMessage("embedderPolicy cannot be null");
+	}
+
+	@Test
+	void writeHeadersWhenDefaultValuesThenDontWriteHeaders() {
+		this.writer.writeHeaders(this.request, this.response);
+		assertThat(this.response.getHeaderNames()).hasSize(0);
+	}
+
+	@Test
+	void writeHeadersWhenResponseHeaderExistsThenDontOverride() {
+		this.response.addHeader(EMBEDDER_HEADER_NAME, "require-corp");
+		this.writer.setPolicy(CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy.UNSAFE_NONE);
+		this.writer.writeHeaders(this.request, this.response);
+		assertThat(this.response.getHeader(EMBEDDER_HEADER_NAME)).isEqualTo("require-corp");
+	}
+
+	@Test
+	void writeHeadersWhenSetHeaderValuesThenWrites() {
+		this.writer.setPolicy(CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP);
+		this.writer.writeHeaders(this.request, this.response);
+		assertThat(this.response.getHeader(EMBEDDER_HEADER_NAME)).isEqualTo("require-corp");
+	}
+
+	@Test
+	void writeHeadersWhenSetEmbedderPolicyThenWritesEmbedderPolicy() {
+		this.writer.setPolicy(CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy.UNSAFE_NONE);
+		this.writer.writeHeaders(this.request, this.response);
+		assertThat(this.response.getHeaderNames()).hasSize(1);
+		assertThat(this.response.getHeader(EMBEDDER_HEADER_NAME)).isEqualTo("unsafe-none");
+	}
+
+}

+ 80 - 0
web/src/test/java/org/springframework/security/web/header/writers/CrossOriginOpenerPolicyHeaderWriterTests.java

@@ -0,0 +1,80 @@
+/*
+ * Copyright 2002-2021 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
+ *
+ *      https://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.header.writers;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+
+class CrossOriginOpenerPolicyHeaderWriterTests {
+
+	private static final String OPENER_HEADER_NAME = "Cross-Origin-Opener-Policy";
+
+	private CrossOriginOpenerPolicyHeaderWriter writer;
+
+	private MockHttpServletRequest request;
+
+	private MockHttpServletResponse response;
+
+	@BeforeEach
+	void setup() {
+		this.writer = new CrossOriginOpenerPolicyHeaderWriter();
+		this.request = new MockHttpServletRequest();
+		this.response = new MockHttpServletResponse();
+	}
+
+	@Test
+	void setOpenerPolicyWhenNullOpenerPolicyThenThrowsIllegalArgument() {
+		assertThatIllegalArgumentException().isThrownBy(() -> this.writer.setPolicy(null))
+				.withMessage("openerPolicy cannot be null");
+	}
+
+	@Test
+	void writeHeadersWhenDefaultValuesThenDontWriteHeaders() {
+		this.writer.writeHeaders(this.request, this.response);
+		assertThat(this.response.getHeaderNames()).hasSize(0);
+	}
+
+	@Test
+	void writeHeadersWhenResponseHeaderExistsThenDontOverride() {
+		this.response.addHeader(OPENER_HEADER_NAME, "same-origin");
+		this.writer.setPolicy(CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS);
+		this.writer.writeHeaders(this.request, this.response);
+		assertThat(this.response.getHeader(OPENER_HEADER_NAME)).isEqualTo("same-origin");
+	}
+
+	@Test
+	void writeHeadersWhenSetHeaderValuesThenWrites() {
+		this.writer.setPolicy(CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS);
+		this.writer.writeHeaders(this.request, this.response);
+		assertThat(this.response.getHeader(OPENER_HEADER_NAME)).isEqualTo("same-origin-allow-popups");
+	}
+
+	@Test
+	void writeHeadersWhenSetOpenerPolicyThenWritesOpenerPolicy() {
+		this.writer.setPolicy(CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS);
+		this.writer.writeHeaders(this.request, this.response);
+		assertThat(this.response.getHeaderNames()).hasSize(1);
+		assertThat(this.response.getHeader(OPENER_HEADER_NAME)).isEqualTo("same-origin-allow-popups");
+	}
+
+}

+ 80 - 0
web/src/test/java/org/springframework/security/web/header/writers/CrossOriginResourcePolicyHeaderWriterTests.java

@@ -0,0 +1,80 @@
+/*
+ * Copyright 2002-2021 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
+ *
+ *      https://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.header.writers;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+
+class CrossOriginResourcePolicyHeaderWriterTests {
+
+	private static final String RESOURCE_HEADER_NAME = "Cross-Origin-Resource-Policy";
+
+	private CrossOriginResourcePolicyHeaderWriter writer;
+
+	private MockHttpServletRequest request;
+
+	private MockHttpServletResponse response;
+
+	@BeforeEach
+	void setup() {
+		this.writer = new CrossOriginResourcePolicyHeaderWriter();
+		this.request = new MockHttpServletRequest();
+		this.response = new MockHttpServletResponse();
+	}
+
+	@Test
+	void setResourcePolicyWhenNullThenThrowsIllegalArgument() {
+		assertThatIllegalArgumentException().isThrownBy(() -> this.writer.setPolicy(null))
+				.withMessage("resourcePolicy cannot be null");
+	}
+
+	@Test
+	void writeHeadersWhenDefaultValuesThenDontWriteHeaders() {
+		this.writer.writeHeaders(this.request, this.response);
+		assertThat(this.response.getHeaderNames()).hasSize(0);
+	}
+
+	@Test
+	void writeHeadersWhenResponseHeaderExistsThenDontOverride() {
+		this.response.addHeader(RESOURCE_HEADER_NAME, "same-site");
+		this.writer.setPolicy(CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy.CROSS_ORIGIN);
+		this.writer.writeHeaders(this.request, this.response);
+		assertThat(this.response.getHeader(RESOURCE_HEADER_NAME)).isEqualTo("same-site");
+	}
+
+	@Test
+	void writeHeadersWhenSetHeaderValuesThenWrites() {
+		this.writer.setPolicy(CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy.SAME_ORIGIN);
+		this.writer.writeHeaders(this.request, this.response);
+		assertThat(this.response.getHeader(RESOURCE_HEADER_NAME)).isEqualTo("same-origin");
+	}
+
+	@Test
+	void writeHeadersWhenSetResourcePolicyThenWritesResourcePolicy() {
+		this.writer.setPolicy(CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy.SAME_SITE);
+		this.writer.writeHeaders(this.request, this.response);
+		assertThat(this.response.getHeaderNames()).hasSize(1);
+		assertThat(this.response.getHeader(RESOURCE_HEADER_NAME)).isEqualTo("same-site");
+	}
+
+}

+ 76 - 0
web/src/test/java/org/springframework/security/web/server/header/CrossOriginEmbedderPolicyServerHttpHeadersWriterTests.java

@@ -0,0 +1,76 @@
+/*
+ * Copyright 2002-2021 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
+ *
+ *      https://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.server.header;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
+import org.springframework.mock.web.server.MockServerWebExchange;
+import org.springframework.web.server.ServerWebExchange;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+
+class CrossOriginEmbedderPolicyServerHttpHeadersWriterTests {
+
+	private ServerWebExchange exchange;
+
+	private CrossOriginEmbedderPolicyServerHttpHeadersWriter writer;
+
+	@BeforeEach
+	void setup() {
+		this.exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/"));
+		this.writer = new CrossOriginEmbedderPolicyServerHttpHeadersWriter();
+	}
+
+	@Test
+	void setEmbedderPolicyWhenNullEmbedderPolicyThenThrowsIllegalArgument() {
+		assertThatIllegalArgumentException().isThrownBy(() -> this.writer.setPolicy(null))
+				.withMessage("embedderPolicy cannot be null");
+	}
+
+	@Test
+	void writeHeadersWhenNoValuesThenDoesNotWriteHeaders() {
+		this.writer.writeHttpHeaders(this.exchange);
+		HttpHeaders headers = this.exchange.getResponse().getHeaders();
+		assertThat(headers).isEmpty();
+	}
+
+	@Test
+	void writeHeadersWhenResponseHeaderExistsThenDontOverride() {
+		this.exchange.getResponse().getHeaders().add(CrossOriginEmbedderPolicyServerHttpHeadersWriter.EMBEDDER_POLICY,
+				"require-corp");
+		this.writer.writeHttpHeaders(this.exchange);
+		HttpHeaders headers = this.exchange.getResponse().getHeaders();
+		assertThat(headers).hasSize(1);
+		assertThat(headers.get(CrossOriginEmbedderPolicyServerHttpHeadersWriter.EMBEDDER_POLICY))
+				.containsOnly("require-corp");
+	}
+
+	@Test
+	void writeHeadersWhenSetHeaderValuesThenWrites() {
+		this.writer.setPolicy(CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP);
+		this.writer.writeHttpHeaders(this.exchange);
+		HttpHeaders headers = this.exchange.getResponse().getHeaders();
+		assertThat(headers).hasSize(1);
+		assertThat(headers.get(CrossOriginEmbedderPolicyServerHttpHeadersWriter.EMBEDDER_POLICY))
+				.containsOnly("require-corp");
+	}
+
+}

+ 77 - 0
web/src/test/java/org/springframework/security/web/server/header/CrossOriginOpenerPolicyServerHttpHeadersWriterTests.java

@@ -0,0 +1,77 @@
+/*
+ * Copyright 2002-2021 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
+ *
+ *      https://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.server.header;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
+import org.springframework.mock.web.server.MockServerWebExchange;
+import org.springframework.web.server.ServerWebExchange;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+
+class CrossOriginOpenerPolicyServerHttpHeadersWriterTests {
+
+	private ServerWebExchange exchange;
+
+	private CrossOriginOpenerPolicyServerHttpHeadersWriter writer;
+
+	@BeforeEach
+	void setup() {
+		this.exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/"));
+		this.writer = new CrossOriginOpenerPolicyServerHttpHeadersWriter();
+	}
+
+	@Test
+	void setOpenerPolicyWhenNullOpenerPolicyThenThrowsIllegalArgument() {
+		assertThatIllegalArgumentException().isThrownBy(() -> this.writer.setPolicy(null))
+				.withMessage("openerPolicy cannot be null");
+	}
+
+	@Test
+	void writeHeadersWhenNoValuesThenDoesNotWriteHeaders() {
+		this.writer.writeHttpHeaders(this.exchange);
+		HttpHeaders headers = this.exchange.getResponse().getHeaders();
+		assertThat(headers).isEmpty();
+	}
+
+	@Test
+	void writeHeadersWhenResponseHeaderExistsThenDontOverride() {
+		this.exchange.getResponse().getHeaders().add(CrossOriginOpenerPolicyServerHttpHeadersWriter.OPENER_POLICY,
+				"same-origin");
+		this.writer.writeHttpHeaders(this.exchange);
+		HttpHeaders headers = this.exchange.getResponse().getHeaders();
+		assertThat(headers).hasSize(1);
+		assertThat(headers.get(CrossOriginOpenerPolicyServerHttpHeadersWriter.OPENER_POLICY))
+				.containsOnly("same-origin");
+	}
+
+	@Test
+	void writeHeadersWhenSetHeaderValuesThenWrites() {
+		this.writer.setPolicy(
+				CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS);
+		this.writer.writeHttpHeaders(this.exchange);
+		HttpHeaders headers = this.exchange.getResponse().getHeaders();
+		assertThat(headers).hasSize(1);
+		assertThat(headers.get(CrossOriginOpenerPolicyServerHttpHeadersWriter.OPENER_POLICY))
+				.containsOnly("same-origin-allow-popups");
+	}
+
+}

+ 76 - 0
web/src/test/java/org/springframework/security/web/server/header/CrossOriginResourcePolicyServerHttpHeadersWriterTests.java

@@ -0,0 +1,76 @@
+/*
+ * Copyright 2002-2021 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
+ *
+ *      https://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.server.header;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
+import org.springframework.mock.web.server.MockServerWebExchange;
+import org.springframework.web.server.ServerWebExchange;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+
+class CrossOriginResourcePolicyServerHttpHeadersWriterTests {
+
+	private ServerWebExchange exchange;
+
+	private CrossOriginResourcePolicyServerHttpHeadersWriter writer;
+
+	@BeforeEach
+	void setup() {
+		this.exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/"));
+		this.writer = new CrossOriginResourcePolicyServerHttpHeadersWriter();
+	}
+
+	@Test
+	void setResourcePolicyWhenNullThenThrowsIllegalArgument() {
+		assertThatIllegalArgumentException().isThrownBy(() -> this.writer.setPolicy(null))
+				.withMessage("resourcePolicy cannot be null");
+	}
+
+	@Test
+	void writeHeadersWhenNoValuesThenDoesNotWriteHeaders() {
+		this.writer.writeHttpHeaders(this.exchange);
+		HttpHeaders headers = this.exchange.getResponse().getHeaders();
+		assertThat(headers).isEmpty();
+	}
+
+	@Test
+	void writeHeadersWhenResponseHeaderExistsThenDontOverride() {
+		this.exchange.getResponse().getHeaders().add(CrossOriginResourcePolicyServerHttpHeadersWriter.RESOURCE_POLICY,
+				"same-origin");
+		this.writer.writeHttpHeaders(this.exchange);
+		HttpHeaders headers = this.exchange.getResponse().getHeaders();
+		assertThat(headers).hasSize(1);
+		assertThat(headers.get(CrossOriginResourcePolicyServerHttpHeadersWriter.RESOURCE_POLICY))
+				.containsOnly("same-origin");
+	}
+
+	@Test
+	void writeHeadersWhenSetHeaderValuesThenWrites() {
+		this.writer.setPolicy(CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy.SAME_ORIGIN);
+		this.writer.writeHttpHeaders(this.exchange);
+		HttpHeaders headers = this.exchange.getResponse().getHeaders();
+		assertThat(headers).hasSize(1);
+		assertThat(headers.get(CrossOriginResourcePolicyServerHttpHeadersWriter.RESOURCE_POLICY))
+				.containsOnly("same-origin");
+	}
+
+}