diff --git a/config/src/test/groovy/org/springframework/security/config/http/HttpHeadersConfigTests.groovy b/config/src/test/groovy/org/springframework/security/config/http/HttpHeadersConfigTests.groovy
index 062c1ede90..82179e3c14 100644
--- a/config/src/test/groovy/org/springframework/security/config/http/HttpHeadersConfigTests.groovy
+++ b/config/src/test/groovy/org/springframework/security/config/http/HttpHeadersConfigTests.groovy
@@ -17,6 +17,7 @@ import org.springframework.security.util.FieldUtils
import javax.servlet.Filter
import javax.servlet.http.HttpServletRequest
+import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException
import org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException;
import org.springframework.mock.web.MockFilterChain
@@ -56,11 +57,10 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
createAppContext()
def hf = getFilter(HeadersFilter)
- MockHttpServletResponse response = new MockHttpServletResponse();
- hf.doFilter(new MockHttpServletRequest(), response);
+ MockHttpServletResponse response = new MockHttpServletResponse()
+ hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
expect:
- hf
response.headers.isEmpty()
}
@@ -73,11 +73,11 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
createAppContext()
def hf = getFilter(HeadersFilter)
- MockHttpServletResponse response = new MockHttpServletResponse();
- hf.doFilter(new MockHttpServletRequest(), response);
+ MockHttpServletResponse response = new MockHttpServletResponse()
+ hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
+
expect:
- hf
- response.headers == ['X-Content-Type-Options':'nosniff']
+ assertHeaders(response, ['X-Content-Type-Options':'nosniff'])
}
def 'http headers frame-options defaults to DENY'() {
@@ -89,10 +89,11 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
createAppContext()
def hf = getFilter(HeadersFilter)
+ MockHttpServletResponse response = new MockHttpServletResponse()
+ hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
expect:
- hf
- hf.headers == ['X-Frame-Options':'DENY']
+ assertHeaders(response, ['X-Frame-Options':'DENY'])
}
def 'http headers frame-options DENY'() {
@@ -104,10 +105,11 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
createAppContext()
def hf = getFilter(HeadersFilter)
+ MockHttpServletResponse response = new MockHttpServletResponse()
+ hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
expect:
- hf
- hf.headers == ['X-Frame-Options':'DENY']
+ assertHeaders(response, ['X-Frame-Options':'DENY'])
}
def 'http headers frame-options SAMEORIGIN'() {
@@ -119,17 +121,18 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
createAppContext()
def hf = getFilter(HeadersFilter)
+ MockHttpServletResponse response = new MockHttpServletResponse()
+ hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
expect:
- hf
- hf.headers == ['X-Frame-Options':'SAMEORIGIN']
+ assertHeaders(response, ['X-Frame-Options':'SAMEORIGIN'])
}
def 'http headers frame-options ALLOW-FROM no origin reports error'() {
when:
httpAutoConfig {
'headers'() {
- 'frame-options'(policy : 'ALLOW-FROM')
+ 'frame-options'(policy : 'ALLOW-FROM', strategy : 'static')
}
}
createAppContext()
@@ -138,14 +141,14 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
then:
BeanDefinitionParsingException e = thrown()
- e.message.contains ' requires a non-empty string value for the origin attribute to be specified.'
+ e.message.contains "Strategy requires a 'value' to be set." // FIME better error message?
}
def 'http headers frame-options ALLOW-FROM spaces only origin reports error'() {
when:
httpAutoConfig {
'headers'() {
- 'frame-options'(policy : 'ALLOW-FROM', origin : ' ')
+ 'frame-options'(policy : 'ALLOW-FROM', strategy: 'static', value : ' ')
}
}
createAppContext()
@@ -154,23 +157,24 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
then:
BeanDefinitionParsingException e = thrown()
- e.message.contains ' requires a non-empty string value for the origin attribute to be specified.'
+ e.message.contains "Strategy requires a 'value' to be set." // FIME better error message?
}
def 'http headers frame-options ALLOW-FROM'() {
when:
httpAutoConfig {
'headers'() {
- 'frame-options'(policy : 'ALLOW-FROM', origin : 'https://example.com')
+ 'frame-options'(policy : 'ALLOW-FROM', strategy: 'static', value : 'https://example.com')
}
}
createAppContext()
def hf = getFilter(HeadersFilter)
+ MockHttpServletResponse response = new MockHttpServletResponse()
+ hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
then:
- hf
- hf.headers == ['X-Frame-Options':'ALLOW-FROM https://example.com']
+ assertHeaders(response, ['X-Frame-Options':'ALLOW-FROM https://example.com'])
}
def 'http headers header a=b'() {
@@ -183,10 +187,11 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
createAppContext()
def hf = getFilter(HeadersFilter)
+ MockHttpServletResponse response = new MockHttpServletResponse()
+ hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
then:
- hf
- hf.headers == ['a':'b']
+ assertHeaders(response, ['a':'b'])
}
def 'http headers header a=b and c=d'() {
@@ -200,10 +205,11 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
createAppContext()
def hf = getFilter(HeadersFilter)
+ MockHttpServletResponse response = new MockHttpServletResponse()
+ hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
then:
- hf
- hf.headers.sort() == ['a':'b', 'c':'d'].sort()
+ assertHeaders(response , ['a':'b', 'c':'d'])
}
def 'http headers header no name produces error'() {
@@ -216,7 +222,7 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
createAppContext()
then:
- thrown(XmlBeanDefinitionStoreException)
+ thrown(BeanCreationException)
}
def 'http headers header no value produces error'() {
@@ -229,7 +235,7 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
createAppContext()
then:
- thrown(XmlBeanDefinitionStoreException)
+ thrown(BeanCreationException)
}
def 'http headers xss-protection defaults'() {
@@ -242,10 +248,11 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
createAppContext()
def hf = getFilter(HeadersFilter)
+ MockHttpServletResponse response = new MockHttpServletResponse()
+ hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
then:
- hf
- hf.headers == ['X-XSS-Protection':'1; mode=block']
+ assertHeaders(response, ['X-XSS-Protection':'1; mode=block'])
}
def 'http headers xss-protection enabled=true'() {
@@ -258,10 +265,11 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
createAppContext()
def hf = getFilter(HeadersFilter)
+ MockHttpServletResponse response = new MockHttpServletResponse()
+ hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
then:
- hf
- hf.headers == ['X-XSS-Protection':'1; mode=block']
+ assertHeaders(response, ['X-XSS-Protection':'1; mode=block'])
}
def 'http headers xss-protection enabled=false'() {
@@ -274,10 +282,11 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
createAppContext()
def hf = getFilter(HeadersFilter)
+ MockHttpServletResponse response = new MockHttpServletResponse()
+ hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
then:
- hf
- hf.headers == ['X-XSS-Protection':'0']
+ assertHeaders(response, ['X-XSS-Protection':'0'])
}
def 'http headers xss-protection enabled=false and block=true produces error'() {
@@ -295,4 +304,11 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
BeanDefinitionParsingException e = thrown()
e.message.contains ' does not allow block="true".'
}
+
+ def assertHeaders(MockHttpServletResponse response, Map expected) {
+ assert response.headerNames == expected.keySet()
+ expected.each { headerName, value ->
+ assert response.getHeaderValues(headerName) == [value]
+ }
+ }
}
diff --git a/docs/manual/src/docbook/appendix-namespace.xml b/docs/manual/src/docbook/appendix-namespace.xml
index f01b39a1d2..96d9c8afb9 100644
--- a/docs/manual/src/docbook/appendix-namespace.xml
+++ b/docs/manual/src/docbook/appendix-namespace.xml
@@ -319,7 +319,7 @@
including it in a frame it is the same as the one serving the page.
-
+
frame-options-strategy
Select the AllowFromStrategy to use when using the ALLOW-FROM policy.
diff --git a/web/src/main/java/org/springframework/security/web/headers/StaticHeaderFactory.java b/web/src/main/java/org/springframework/security/web/headers/StaticHeaderFactory.java
index db9dd95a8d..c78e908b45 100644
--- a/web/src/main/java/org/springframework/security/web/headers/StaticHeaderFactory.java
+++ b/web/src/main/java/org/springframework/security/web/headers/StaticHeaderFactory.java
@@ -3,6 +3,8 @@ package org.springframework.security.web.headers;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.springframework.util.Assert;
+
/**
* {@code HeaderFactory} implementation which returns the same {@code Header} instance.
*
@@ -14,6 +16,9 @@ public class StaticHeaderFactory implements HeaderFactory {
private final Header header;
public StaticHeaderFactory(String name, String... values) {
+ Assert.hasText(name, "Header name is required");
+ Assert.notEmpty(values, "Header values cannot be null or empty");
+ Assert.noNullElements(values, "Header values cannot contain null values");
header = new Header(name, values);
}
diff --git a/web/src/main/java/org/springframework/security/web/headers/frameoptions/FrameOptionsHeaderFactory.java b/web/src/main/java/org/springframework/security/web/headers/frameoptions/FrameOptionsHeaderFactory.java
index ca6d8e7b40..65ff4c6285 100644
--- a/web/src/main/java/org/springframework/security/web/headers/frameoptions/FrameOptionsHeaderFactory.java
+++ b/web/src/main/java/org/springframework/security/web/headers/frameoptions/FrameOptionsHeaderFactory.java
@@ -33,11 +33,10 @@ public class FrameOptionsHeaderFactory implements HeaderFactory {
this.allowFromStrategy=allowFromStrategy;
}
- @Override
public Header create(HttpServletRequest request, HttpServletResponse response) {
if (ALLOW_FROM.equals(mode)) {
String value = allowFromStrategy.apply(request);
- return new Header(FRAME_OPTIONS_HEADER, value);
+ return new Header(FRAME_OPTIONS_HEADER, ALLOW_FROM + " " + value);
} else {
return new Header(FRAME_OPTIONS_HEADER, mode);
}
diff --git a/web/src/main/java/org/springframework/security/web/headers/frameoptions/NullAllowFromStrategy.java b/web/src/main/java/org/springframework/security/web/headers/frameoptions/NullAllowFromStrategy.java
index d039833ddf..98ee4a720a 100644
--- a/web/src/main/java/org/springframework/security/web/headers/frameoptions/NullAllowFromStrategy.java
+++ b/web/src/main/java/org/springframework/security/web/headers/frameoptions/NullAllowFromStrategy.java
@@ -10,7 +10,6 @@ import javax.servlet.http.HttpServletRequest;
* To change this template use File | Settings | File Templates.
*/
public class NullAllowFromStrategy implements AllowFromStrategy {
- @Override
public String apply(HttpServletRequest request) {
return null;
}
diff --git a/web/src/main/java/org/springframework/security/web/headers/frameoptions/RequestParameterAllowFromStrategy.java b/web/src/main/java/org/springframework/security/web/headers/frameoptions/RequestParameterAllowFromStrategy.java
index f36cc9277d..f7697ef17f 100644
--- a/web/src/main/java/org/springframework/security/web/headers/frameoptions/RequestParameterAllowFromStrategy.java
+++ b/web/src/main/java/org/springframework/security/web/headers/frameoptions/RequestParameterAllowFromStrategy.java
@@ -24,7 +24,6 @@ public abstract class RequestParameterAllowFromStrategy implements AllowFromStra
protected final Log log = LogFactory.getLog(getClass());
- @Override
public String apply(HttpServletRequest request) {
String from = request.getParameter(parameter);
if (log.isDebugEnabled()) {
diff --git a/web/src/main/java/org/springframework/security/web/headers/frameoptions/StaticAllowFromStrategy.java b/web/src/main/java/org/springframework/security/web/headers/frameoptions/StaticAllowFromStrategy.java
index 47108c4e11..39be7fff41 100644
--- a/web/src/main/java/org/springframework/security/web/headers/frameoptions/StaticAllowFromStrategy.java
+++ b/web/src/main/java/org/springframework/security/web/headers/frameoptions/StaticAllowFromStrategy.java
@@ -14,7 +14,6 @@ public class StaticAllowFromStrategy implements AllowFromStrategy {
this.uri=uri;
}
- @Override
public String apply(HttpServletRequest request) {
return uri.toString();
}
diff --git a/web/src/test/java/org/springframework/security/web/headers/HeadersFilterTest.java b/web/src/test/java/org/springframework/security/web/headers/HeadersFilterTest.java
index d916452430..0eb856fd71 100644
--- a/web/src/test/java/org/springframework/security/web/headers/HeadersFilterTest.java
+++ b/web/src/test/java/org/springframework/security/web/headers/HeadersFilterTest.java
@@ -84,7 +84,6 @@ public class HeadersFilterTest {
private String name;
private String value;
- @Override
public Header create(HttpServletRequest request, HttpServletResponse response) {
return new Header(name, value);
}