SEC-2230: Add Header JavaConfig

Added JavaConfig for Headers. In the process, more HeaderWriter instances
were added so that we can reuse logic between the XML and JavaConfig. This
also prompted repackaging the writers.
This commit is contained in:
Rob Winch 2013-07-30 16:56:46 -05:00
parent 988e97e366
commit 606bddf598
38 changed files with 1038 additions and 156 deletions

View File

@ -36,6 +36,7 @@ import org.springframework.security.web.authentication.ui.DefaultLoginPageViewFi
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.authentication.www.DigestAuthenticationFilter; import org.springframework.security.web.authentication.www.DigestAuthenticationFilter;
import org.springframework.security.web.context.SecurityContextPersistenceFilter; import org.springframework.security.web.context.SecurityContextPersistenceFilter;
import org.springframework.security.web.header.HeaderWriterFilter;
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter; import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter; import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter; import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
@ -63,6 +64,8 @@ final class FilterComparator implements Comparator<Filter>, Serializable {
order += STEP; order += STEP;
put(SecurityContextPersistenceFilter.class, order); put(SecurityContextPersistenceFilter.class, order);
order += STEP; order += STEP;
put(HeaderWriterFilter.class, order);
order += STEP;
put(LogoutFilter.class, order); put(LogoutFilter.class, order);
order += STEP; order += STEP;
put(X509AuthenticationFilter.class, order); put(X509AuthenticationFilter.class, order);

View File

@ -41,6 +41,7 @@ import org.springframework.security.config.annotation.web.configurers.ChannelSec
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer; import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer; import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer; import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer;
import org.springframework.security.config.annotation.web.configurers.JeeConfigurer; import org.springframework.security.config.annotation.web.configurers.JeeConfigurer;
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer; import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
@ -239,6 +240,10 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
return getOrApply(new OpenIDLoginConfigurer<HttpSecurity>()); return getOrApply(new OpenIDLoginConfigurer<HttpSecurity>());
} }
public HeadersConfigurer<HttpSecurity> headers() throws Exception {
return getOrApply(new HeadersConfigurer<HttpSecurity>());
}
/** /**
* Allows configuring of Session Management. * Allows configuring of Session Management.
* *

View File

@ -155,6 +155,7 @@ public abstract class WebSecurityConfigurerAdapter implements SecurityConfigurer
if(!disableDefaults) { if(!disableDefaults) {
http http
.exceptionHandling().and() .exceptionHandling().and()
.headers().and()
.sessionManagement().and() .sessionManagement().and()
.securityContext().and() .securityContext().and()
.requestCache().and() .requestCache().and()

View File

@ -0,0 +1,98 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.ArrayList;
import java.util.List;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.header.HeaderWriter;
import org.springframework.security.web.header.HeaderWriterFilter;
import org.springframework.security.web.header.writers.CacheControlHeadersWriter;
import org.springframework.security.web.header.writers.HstsHeaderWriter;
import org.springframework.security.web.header.writers.XContentTypeOptionsHeaderWriter;
import org.springframework.security.web.header.writers.XXssProtectionHeaderWriter;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;
import org.springframework.util.Assert;
/**
* @author Rob Winch
* @since 3.2
* @see RememberMeConfigurer
*/
public final class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<H> {
private List<HeaderWriter> headerWriters = new ArrayList<HeaderWriter>();
/**
* Creates a new instance
* @see HttpSecurity#headers()
*/
public HeadersConfigurer() {
}
/**
* Adds a {@link HeaderWriter} instance
* @param headerWriter the {@link HeaderWriter} instance to add
* @return the {@link HeadersConfigurer} for additional customizations
*/
public HeadersConfigurer<H> addHeaderWriter(HeaderWriter headerWriter) {
Assert.notNull(headerWriter, "headerWriter cannot be null");
this.headerWriters.add(headerWriter);
return this;
}
@Override
public void configure(H http) throws Exception {
HeaderWriterFilter headersFilter = createHeaderWriterFilter();
http.addFilter(headersFilter);
}
/**
* Creates the {@link HeaderWriter}
* @return the {@link HeaderWriter}
*/
private HeaderWriterFilter createHeaderWriterFilter() {
HeaderWriterFilter headersFilter = new HeaderWriterFilter(getHeaderWriters());
headersFilter = postProcess(headersFilter);
return headersFilter;
}
/**
* Gets the {@link HeaderWriter} instances and possibly initializes with the defaults.
* @return
*/
private List<HeaderWriter> getHeaderWriters() {
if(headerWriters.isEmpty()) {
addDefaultHeaderWriters();
}
return headerWriters;
}
/**
* Explicitly adds the default {@link HeaderWriter} instances. If no,
* {@link HeaderWriter} instances have been added this is automatically
* invoked.
*
*/
private void addDefaultHeaderWriters() {
headerWriters.add(new XContentTypeOptionsHeaderWriter());
headerWriters.add(new XXssProtectionHeaderWriter());
headerWriters.add(new CacheControlHeadersWriter());
headerWriters.add(new HstsHeaderWriter());
headerWriters.add(new XFrameOptionsHeaderWriter());
}
}

View File

@ -27,15 +27,17 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext; import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.web.headers.Header; import org.springframework.security.web.header.HeaderWriterFilter;
import org.springframework.security.web.headers.HeadersFilter; import org.springframework.security.web.header.writers.CacheControlHeadersWriter;
import org.springframework.security.web.headers.HstsHeaderWriter; import org.springframework.security.web.header.writers.HstsHeaderWriter;
import org.springframework.security.web.headers.StaticHeadersWriter; import org.springframework.security.web.header.writers.StaticHeadersWriter;
import org.springframework.security.web.headers.frameoptions.AbstractRequestParameterAllowFromStrategy; import org.springframework.security.web.header.writers.XContentTypeOptionsHeaderWriter;
import org.springframework.security.web.headers.frameoptions.RegExpAllowFromStrategy; import org.springframework.security.web.header.writers.XXssProtectionHeaderWriter;
import org.springframework.security.web.headers.frameoptions.StaticAllowFromStrategy; import org.springframework.security.web.header.writers.frameoptions.AbstractRequestParameterAllowFromStrategy;
import org.springframework.security.web.headers.frameoptions.WhiteListedAllowFromStrategy; import org.springframework.security.web.header.writers.frameoptions.RegExpAllowFromStrategy;
import org.springframework.security.web.headers.frameoptions.XFrameOptionsHeaderWriter; import org.springframework.security.web.header.writers.frameoptions.StaticAllowFromStrategy;
import org.springframework.security.web.header.writers.frameoptions.WhiteListedAllowFromStrategy;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils; import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element; import org.w3c.dom.Element;
@ -72,16 +74,13 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
private static final String FRAME_OPTIONS_ELEMENT = "frame-options"; private static final String FRAME_OPTIONS_ELEMENT = "frame-options";
private static final String GENERIC_HEADER_ELEMENT = "header"; private static final String GENERIC_HEADER_ELEMENT = "header";
private static final String XSS_PROTECTION_HEADER = "X-XSS-Protection";
private static final String CONTENT_TYPE_OPTIONS_HEADER = "X-Content-Type-Options";
private static final String ALLOW_FROM = "ALLOW-FROM"; private static final String ALLOW_FROM = "ALLOW-FROM";
private ManagedList<BeanMetadataElement> headerWriters; private ManagedList<BeanMetadataElement> headerWriters;
public BeanDefinition parse(Element element, ParserContext parserContext) { public BeanDefinition parse(Element element, ParserContext parserContext) {
headerWriters = new ManagedList<BeanMetadataElement>(); headerWriters = new ManagedList<BeanMetadataElement>();
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(HeadersFilter.class); BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(HeaderWriterFilter.class);
parseCacheControlElement(element); parseCacheControlElement(element);
parseHstsElement(element); parseHstsElement(element);
@ -100,9 +99,7 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
frameOptions.addConstructorArgValue("DENY"); frameOptions.addConstructorArgValue("DENY");
headerWriters.add(frameOptions.getBeanDefinition()); headerWriters.add(frameOptions.getBeanDefinition());
BeanDefinitionBuilder xss = BeanDefinitionBuilder.genericBeanDefinition(StaticHeadersWriter.class); BeanDefinitionBuilder xss = BeanDefinitionBuilder.genericBeanDefinition(XXssProtectionHeaderWriter.class);
xss.addConstructorArgValue(XSS_PROTECTION_HEADER);
xss.addConstructorArgValue("1; mode=block");
headerWriters.add(xss.getBeanDefinition()); headerWriters.add(xss.getBeanDefinition());
} }
builder.addConstructorArgValue(headerWriters); builder.addConstructorArgValue(headerWriters);
@ -117,28 +114,7 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
} }
private void addCacheControl() { private void addCacheControl() {
ManagedList<BeanDefinition> headers = new ManagedList<BeanDefinition>(); BeanDefinitionBuilder headersWriter = BeanDefinitionBuilder.genericBeanDefinition(CacheControlHeadersWriter.class);
BeanDefinitionBuilder pragmaHeader = BeanDefinitionBuilder.genericBeanDefinition(Header.class);
pragmaHeader.addConstructorArgValue("Pragma");
ManagedList<String> pragmaValues = new ManagedList<String>();
pragmaValues.add("no-cache");
pragmaHeader.addConstructorArgValue(pragmaValues);
headers.add(pragmaHeader.getBeanDefinition());
BeanDefinitionBuilder cacheControlHeader = BeanDefinitionBuilder.genericBeanDefinition(Header.class);
cacheControlHeader.addConstructorArgValue("Cache-Control");
ManagedList<String> cacheControlValues = new ManagedList<String>();
cacheControlValues.add("no-cache");
cacheControlValues.add("no-store");
cacheControlValues.add("max-age=0");
cacheControlValues.add("must-revalidate");
cacheControlHeader.addConstructorArgValue(cacheControlValues);
headers.add(cacheControlHeader.getBeanDefinition());
BeanDefinitionBuilder headersWriter = BeanDefinitionBuilder.genericBeanDefinition(StaticHeadersWriter.class);
headersWriter.addConstructorArgValue(headers);
headerWriters.add(headersWriter.getBeanDefinition()); headerWriters.add(headersWriter.getBeanDefinition());
} }
@ -191,9 +167,7 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
} }
private void addContentTypeOptions() { private void addContentTypeOptions() {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(StaticHeadersWriter.class); BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(XContentTypeOptionsHeaderWriter.class);
builder.addConstructorArgValue(CONTENT_TYPE_OPTIONS_HEADER);
builder.addConstructorArgValue("nosniff");
headerWriters.add(builder.getBeanDefinition()); headerWriters.add(builder.getBeanDefinition());
} }
@ -256,18 +230,16 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
private void parseXssElement(Element element, ParserContext parserContext) { private void parseXssElement(Element element, ParserContext parserContext) {
Element xssElt = DomUtils.getChildElementByTagName(element, XSS_ELEMENT); Element xssElt = DomUtils.getChildElementByTagName(element, XSS_ELEMENT);
if (xssElt != null) { if (xssElt != null) {
boolean enabled = Boolean.valueOf(getAttribute(xssElt, ATT_ENABLED, "true")); String enabled = xssElt.getAttribute(ATT_ENABLED);
boolean block = Boolean.valueOf(getAttribute(xssElt, ATT_BLOCK, enabled ? "true" : "false")); String block = xssElt.getAttribute(ATT_BLOCK);
String value = enabled ? "1" : "0"; BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(XXssProtectionHeaderWriter.class);
if (enabled && block) { if(StringUtils.hasText(enabled)) {
value += "; mode=block"; builder.addPropertyValue("enabled", enabled);
} else if (!enabled && block) { }
parserContext.getReaderContext().error("<xss-protection enabled=\"false\"/> does not allow block=\"true\".", xssElt); if(StringUtils.hasText(block)) {
builder.addPropertyValue("block", block);
} }
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(StaticHeadersWriter.class);
builder.addConstructorArgValue(XSS_PROTECTION_HEADER);
builder.addConstructorArgValue(value);
headerWriters.add(builder.getBeanDefinition()); headerWriters.add(builder.getBeanDefinition());
} }
} }

View File

@ -84,6 +84,14 @@ abstract class BaseSpringSpec extends Specification {
context.getBean("springSecurityFilterChain",Filter.class) context.getBean("springSecurityFilterChain",Filter.class)
} }
def getResponseHeaders() {
def headers = [:]
response.headerNames.each { name ->
headers.put(name, response.getHeaderValues(name).join(','))
}
return headers
}
AuthenticationManager authenticationManager() { AuthenticationManager authenticationManager() {
context.getBean(AuthenticationManager) context.getBean(AuthenticationManager)
} }

View File

@ -66,6 +66,39 @@ class WebSecurityConfigurerAdapterTests extends BaseSpringSpec {
authenticationManager.messages.messageSource instanceof ApplicationContext authenticationManager.messages.messageSource instanceof ApplicationContext
} }
def "headers are populated by default"() {
setup: "load config that overrides http and accepts defaults"
loadConfig(HeadersArePopulatedByDefaultConfig)
request.secure = true
when: "invoke the springSecurityFilterChain"
springSecurityFilterChain.doFilter(request, response, chain)
then: "the default headers are added"
responseHeaders == ['X-Content-Type-Options':'nosniff',
'X-Frame-Options':'DENY',
'Strict-Transport-Security': 'max-age=31536000 ; includeSubDomains',
'Cache-Control': 'no-cache,no-store,max-age=0,must-revalidate',
'Pragma':'no-cache',
'X-XSS-Protection' : '1; mode=block']
}
@EnableWebSecurity
@Configuration
static class HeadersArePopulatedByDefaultConfig extends WebSecurityConfigurerAdapter {
@Override
protected void registerAuthentication(AuthenticationManagerBuilder auth)
throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
}
@Override
protected void configure(HttpSecurity http) throws Exception {
}
}
def "AuthenticationEventPublisher is registered for Web registerAuthentication"() { def "AuthenticationEventPublisher is registered for Web registerAuthentication"() {
when: when:
loadConfig(InMemoryAuthWithWebSecurityConfigurerAdapter) loadConfig(InMemoryAuthWithWebSecurityConfigurerAdapter)

View File

@ -23,12 +23,12 @@ import org.springframework.mock.web.MockFilterChain
import org.springframework.mock.web.MockHttpServletRequest import org.springframework.mock.web.MockHttpServletRequest
import org.springframework.mock.web.MockHttpServletResponse import org.springframework.mock.web.MockHttpServletResponse
import org.springframework.security.config.annotation.BaseSpringSpec import org.springframework.security.config.annotation.BaseSpringSpec
import org.springframework.security.config.annotation.web.WebSecurityConfigurer; import org.springframework.security.config.annotation.web.WebSecurityConfigurer
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.builders.WebSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig; import org.springframework.security.config.annotation.web.configuration.BaseWebConfig
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.web.DefaultSecurityFilterChain import org.springframework.security.web.DefaultSecurityFilterChain
import org.springframework.security.web.FilterChainProxy import org.springframework.security.web.FilterChainProxy
import org.springframework.security.web.access.ExceptionTranslationFilter import org.springframework.security.web.access.ExceptionTranslationFilter
@ -37,9 +37,10 @@ import org.springframework.security.web.authentication.AnonymousAuthenticationFi
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
import org.springframework.security.web.authentication.logout.LogoutFilter import org.springframework.security.web.authentication.logout.LogoutFilter
import org.springframework.security.web.context.SecurityContextPersistenceFilter import org.springframework.security.web.context.SecurityContextPersistenceFilter
import org.springframework.security.web.header.HeaderWriterFilter
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter import org.springframework.security.web.savedrequest.RequestCacheAwareFilter
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
import org.springframework.security.web.session.SessionManagementFilter; import org.springframework.security.web.session.SessionManagementFilter
import org.springframework.security.web.util.AnyRequestMatcher import org.springframework.security.web.util.AnyRequestMatcher
/** /**
@ -113,7 +114,7 @@ class DefaultFiltersTests extends BaseSpringSpec {
filterChains[0].filters.empty filterChains[0].filters.empty
filterChains[1].requestMatcher instanceof AnyRequestMatcher filterChains[1].requestMatcher instanceof AnyRequestMatcher
filterChains[1].filters.collect { it.class } == filterChains[1].filters.collect { it.class } ==
[SecurityContextPersistenceFilter, LogoutFilter, RequestCacheAwareFilter, [SecurityContextPersistenceFilter, HeaderWriterFilter, LogoutFilter, RequestCacheAwareFilter,
SecurityContextHolderAwareRequestFilter, AnonymousAuthenticationFilter, SessionManagementFilter, SecurityContextHolderAwareRequestFilter, AnonymousAuthenticationFilter, SessionManagementFilter,
ExceptionTranslationFilter, FilterSecurityInterceptor ] ExceptionTranslationFilter, FilterSecurityInterceptor ]
} }

View File

@ -40,6 +40,7 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
import org.springframework.security.web.authentication.logout.LogoutFilter import org.springframework.security.web.authentication.logout.LogoutFilter
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy
import org.springframework.security.web.context.SecurityContextPersistenceFilter import org.springframework.security.web.context.SecurityContextPersistenceFilter
import org.springframework.security.web.header.HeaderWriterFilter
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter import org.springframework.security.web.savedrequest.RequestCacheAwareFilter
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
import org.springframework.security.web.session.SessionManagementFilter import org.springframework.security.web.session.SessionManagementFilter
@ -62,7 +63,7 @@ class FormLoginConfigurerTests extends BaseSpringSpec {
filterChains[0].filters.empty filterChains[0].filters.empty
filterChains[1].requestMatcher instanceof AnyRequestMatcher filterChains[1].requestMatcher instanceof AnyRequestMatcher
filterChains[1].filters.collect { it.class.name.contains('$') ? it.class.superclass : it.class } == filterChains[1].filters.collect { it.class.name.contains('$') ? it.class.superclass : it.class } ==
[SecurityContextPersistenceFilter, LogoutFilter, UsernamePasswordAuthenticationFilter, [SecurityContextPersistenceFilter, HeaderWriterFilter, LogoutFilter, UsernamePasswordAuthenticationFilter,
RequestCacheAwareFilter, SecurityContextHolderAwareRequestFilter, RequestCacheAwareFilter, SecurityContextHolderAwareRequestFilter,
AnonymousAuthenticationFilter, SessionManagementFilter, ExceptionTranslationFilter, FilterSecurityInterceptor ] AnonymousAuthenticationFilter, SessionManagementFilter, ExceptionTranslationFilter, FilterSecurityInterceptor ]

View File

@ -0,0 +1,248 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.BaseSpringSpec
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig
import org.springframework.security.web.header.writers.CacheControlHeadersWriter
import org.springframework.security.web.header.writers.HstsHeaderWriter
import org.springframework.security.web.header.writers.StaticHeadersWriter
import org.springframework.security.web.header.writers.XContentTypeOptionsHeaderWriter
import org.springframework.security.web.header.writers.XXssProtectionHeaderWriter
import org.springframework.security.web.header.writers.frameoptions.StaticAllowFromStrategy
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode
import org.springframework.security.web.util.AnyRequestMatcher
/**
* Tests to verify that all the functionality of <headers> attributes is present
*
* @author Rob Winch
*
*/
public class NamespaceHttpHeadersTests extends BaseSpringSpec {
def "http/headers"() {
setup:
loadConfig(HeadersDefaultConfig)
request.secure = true
when:
springSecurityFilterChain.doFilter(request,response,chain)
then:
responseHeaders == ['X-Content-Type-Options':'nosniff',
'X-Frame-Options':'DENY',
'Strict-Transport-Security': 'max-age=31536000 ; includeSubDomains',
'Cache-Control': 'no-cache,no-store,max-age=0,must-revalidate',
'Pragma':'no-cache',
'X-XSS-Protection' : '1; mode=block']
}
@Configuration
static class HeadersDefaultConfig extends BaseWebConfig {
@Override
protected void configure(HttpSecurity http) {
http
.headers()
}
}
def "http/headers/cache-control"() {
setup:
loadConfig(HeadersCacheControlConfig)
request.secure = true
when:
springSecurityFilterChain.doFilter(request,response,chain)
then:
responseHeaders == ['Cache-Control': 'no-cache,no-store,max-age=0,must-revalidate',
'Pragma':'no-cache']
}
@Configuration
static class HeadersCacheControlConfig extends BaseWebConfig {
@Override
protected void configure(HttpSecurity http) {
http
.headers()
.addHeaderWriter(new CacheControlHeadersWriter())
}
}
def "http/headers/hsts"() {
setup:
loadConfig(HstsConfig)
request.secure = true
when:
springSecurityFilterChain.doFilter(request,response,chain)
then:
responseHeaders == ['Strict-Transport-Security': 'max-age=31536000 ; includeSubDomains']
}
@Configuration
static class HstsConfig extends BaseWebConfig {
@Override
protected void configure(HttpSecurity http) {
http
.headers()
.addHeaderWriter(new HstsHeaderWriter())
}
}
def "http/headers/hsts custom"() {
setup:
loadConfig(HstsCustomConfig)
when:
springSecurityFilterChain.doFilter(request,response,chain)
then:
responseHeaders == ['Strict-Transport-Security': 'max-age=15768000']
}
@Configuration
static class HstsCustomConfig extends BaseWebConfig {
@Override
protected void configure(HttpSecurity http) {
http
.headers()
// hsts@request-matcher-ref, hsts@max-age-seconds, hsts@include-subdomains
// Additional Constructors are provided to leverage default values
.addHeaderWriter(new HstsHeaderWriter(new AnyRequestMatcher(), 15768000, false))
}
}
def "http/headers/frame-options@policy=SAMEORIGIN"() {
setup:
loadConfig(FrameOptionsSameOriginConfig)
when:
springSecurityFilterChain.doFilter(request,response,chain)
then:
responseHeaders == ['X-Frame-Options': 'SAMEORIGIN']
}
@Configuration
static class FrameOptionsSameOriginConfig extends BaseWebConfig {
@Override
protected void configure(HttpSecurity http) {
http
.headers()
// frame-options@policy=SAMEORIGIN
.addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN))
}
}
// frame-options@strategy, frame-options@value, frame-options@parameter are not provided instead use frame-options@ref
def "http/headers/frame-options"() {
setup:
loadConfig(FrameOptionsAllowFromConfig)
when:
springSecurityFilterChain.doFilter(request,response,chain)
then:
responseHeaders == ['X-Frame-Options': 'ALLOW-FROM https://example.com']
}
@Configuration
static class FrameOptionsAllowFromConfig extends BaseWebConfig {
@Override
protected void configure(HttpSecurity http) {
http
.headers()
// frame-options@ref
.addHeaderWriter(new XFrameOptionsHeaderWriter(new StaticAllowFromStrategy(new URI("https://example.com"))))
}
}
def "http/headers/xss-protection"() {
setup:
loadConfig(XssProtectionConfig)
when:
springSecurityFilterChain.doFilter(request,response,chain)
then:
responseHeaders == ['X-XSS-Protection': '1; mode=block']
}
@Configuration
static class XssProtectionConfig extends BaseWebConfig {
@Override
protected void configure(HttpSecurity http) {
http
.headers()
// xss-protection
.addHeaderWriter(new XXssProtectionHeaderWriter())
}
}
def "http/headers/xss-protection custom"() {
setup:
loadConfig(XssProtectionCustomConfig)
when:
springSecurityFilterChain.doFilter(request,response,chain)
then:
responseHeaders == ['X-XSS-Protection': '1']
}
@Configuration
static class XssProtectionCustomConfig extends BaseWebConfig {
@Override
protected void configure(HttpSecurity http) {
http
.headers()
// xss-protection@enabled and xss-protection@block
.addHeaderWriter(new XXssProtectionHeaderWriter(enabled:true,block:false))
}
}
def "http/headers/content-type-options"() {
setup:
loadConfig(ContentTypeOptionsConfig)
when:
springSecurityFilterChain.doFilter(request,response,chain)
then:
responseHeaders == ['X-Content-Type-Options': 'nosniff']
}
@Configuration
static class ContentTypeOptionsConfig extends BaseWebConfig {
@Override
protected void configure(HttpSecurity http) {
http
.headers()
// content-type-options
.addHeaderWriter(new XContentTypeOptionsHeaderWriter())
}
}
// header@name / header@value are not provided instead use header@ref
def "http/headers/header@ref"() {
setup:
loadConfig(HeaderRefConfig)
when:
springSecurityFilterChain.doFilter(request,response,chain)
then:
responseHeaders == ['customHeaderName': 'customHeaderValue']
}
@Configuration
static class HeaderRefConfig extends BaseWebConfig {
@Override
protected void configure(HttpSecurity http) {
http
.headers()
.addHeaderWriter(new StaticHeadersWriter("customHeaderName", "customHeaderValue"))
}
}
}

View File

@ -12,30 +12,15 @@
*/ */
package org.springframework.security.config.http package org.springframework.security.config.http
import org.springframework.security.util.FieldUtils import org.springframework.beans.factory.BeanCreationException
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.parsing.BeanDefinitionParsingException
import org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException;
import org.springframework.mock.web.MockFilterChain import org.springframework.mock.web.MockFilterChain
import org.springframework.mock.web.MockHttpServletRequest import org.springframework.mock.web.MockHttpServletRequest
import org.springframework.mock.web.MockHttpServletResponse import org.springframework.mock.web.MockHttpServletResponse
import org.springframework.security.config.BeanIds import org.springframework.security.web.FilterChainProxy
import org.springframework.security.openid.OpenIDAuthenticationFilter import org.springframework.security.web.header.HeaderWriterFilter
import org.springframework.security.openid.OpenIDAuthenticationToken import org.springframework.security.web.header.writers.StaticHeadersWriter
import org.springframework.security.openid.OpenIDConsumer import org.springframework.security.web.util.AnyRequestMatcher
import org.springframework.security.openid.OpenIDConsumerException
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.access.ExceptionTranslationFilter
import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter
import org.springframework.security.web.headers.HeadersFilter
import org.springframework.security.web.headers.StaticHeadersWriter;
import org.springframework.security.web.headers.frameoptions.StaticAllowFromStrategy;
import org.springframework.security.web.util.AnyRequestMatcher;
/** /**
* *
@ -48,7 +33,7 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
} }
createAppContext() createAppContext()
def hf = getFilter(HeadersFilter) def hf = getFilter(HeaderWriterFilter)
expect: expect:
!hf !hf
@ -61,7 +46,7 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
} }
createAppContext() createAppContext()
when: when:
def hf = getFilter(HeadersFilter) def hf = getFilter(HeaderWriterFilter)
MockHttpServletResponse response = new MockHttpServletResponse() MockHttpServletResponse response = new MockHttpServletResponse()
hf.doFilter(new MockHttpServletRequest(secure:true), response, new MockFilterChain()) hf.doFilter(new MockHttpServletRequest(secure:true), response, new MockFilterChain())
then: then:
@ -81,7 +66,7 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
} }
createAppContext() createAppContext()
def hf = getFilter(HeadersFilter) def hf = getFilter(HeaderWriterFilter)
MockHttpServletResponse response = new MockHttpServletResponse() MockHttpServletResponse response = new MockHttpServletResponse()
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain()) hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
@ -97,7 +82,7 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
} }
createAppContext() createAppContext()
def hf = getFilter(HeadersFilter) def hf = getFilter(HeaderWriterFilter)
MockHttpServletResponse response = new MockHttpServletResponse() MockHttpServletResponse response = new MockHttpServletResponse()
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain()) hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
@ -113,7 +98,7 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
} }
createAppContext() createAppContext()
def hf = getFilter(HeadersFilter) def hf = getFilter(HeaderWriterFilter)
MockHttpServletResponse response = new MockHttpServletResponse() MockHttpServletResponse response = new MockHttpServletResponse()
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain()) hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
@ -129,7 +114,7 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
} }
createAppContext() createAppContext()
def hf = getFilter(HeadersFilter) def hf = getFilter(HeaderWriterFilter)
MockHttpServletResponse response = new MockHttpServletResponse() MockHttpServletResponse response = new MockHttpServletResponse()
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain()) hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
@ -146,7 +131,7 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
} }
createAppContext() createAppContext()
def hf = getFilter(HeadersFilter) def hf = getFilter(HeaderWriterFilter)
then: then:
BeanDefinitionParsingException e = thrown() BeanDefinitionParsingException e = thrown()
@ -162,7 +147,7 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
} }
createAppContext() createAppContext()
def hf = getFilter(HeadersFilter) def hf = getFilter(HeaderWriterFilter)
then: then:
BeanDefinitionParsingException e = thrown() BeanDefinitionParsingException e = thrown()
@ -178,7 +163,7 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
} }
createAppContext() createAppContext()
def hf = getFilter(HeadersFilter) def hf = getFilter(HeaderWriterFilter)
MockHttpServletResponse response = new MockHttpServletResponse() MockHttpServletResponse response = new MockHttpServletResponse()
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain()) hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
@ -195,7 +180,7 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
} }
createAppContext() createAppContext()
def hf = getFilter(HeadersFilter) def hf = getFilter(HeaderWriterFilter)
MockHttpServletResponse response = new MockHttpServletResponse() MockHttpServletResponse response = new MockHttpServletResponse()
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain()) hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
@ -213,7 +198,7 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
} }
createAppContext() createAppContext()
def hf = getFilter(HeadersFilter) def hf = getFilter(HeaderWriterFilter)
MockHttpServletResponse response = new MockHttpServletResponse() MockHttpServletResponse response = new MockHttpServletResponse()
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain()) hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
@ -234,7 +219,7 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
} }
createAppContext() createAppContext()
when: when:
def hf = getFilter(HeadersFilter) def hf = getFilter(HeaderWriterFilter)
MockHttpServletResponse response = new MockHttpServletResponse() MockHttpServletResponse response = new MockHttpServletResponse()
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain()) hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
then: then:
@ -276,7 +261,7 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
} }
createAppContext() createAppContext()
def hf = getFilter(HeadersFilter) def hf = getFilter(HeaderWriterFilter)
MockHttpServletResponse response = new MockHttpServletResponse() MockHttpServletResponse response = new MockHttpServletResponse()
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain()) hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
@ -293,7 +278,7 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
} }
createAppContext() createAppContext()
def hf = getFilter(HeadersFilter) def hf = getFilter(HeaderWriterFilter)
MockHttpServletResponse response = new MockHttpServletResponse() MockHttpServletResponse response = new MockHttpServletResponse()
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain()) hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
@ -310,7 +295,7 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
} }
createAppContext() createAppContext()
def hf = getFilter(HeadersFilter) def hf = getFilter(HeaderWriterFilter)
MockHttpServletResponse response = new MockHttpServletResponse() MockHttpServletResponse response = new MockHttpServletResponse()
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain()) hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
@ -327,11 +312,11 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
} }
createAppContext() createAppContext()
def hf = getFilter(HeadersFilter) def hf = getFilter(HeaderWriterFilter)
then: then:
BeanDefinitionParsingException e = thrown() BeanCreationException e = thrown()
e.message.contains '<xss-protection enabled="false"/> does not allow block="true".' e.message.contains 'Cannot set block to true with enabled false'
} }
def 'http headers cache-control'() { def 'http headers cache-control'() {

View File

@ -1,4 +1,4 @@
package org.springframework.security.web.headers; package org.springframework.security.web.header;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.security.web.headers; package org.springframework.security.web.header;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -21,7 +21,7 @@ import javax.servlet.http.HttpServletResponse;
/** /**
* Contract for writing headers to a {@link HttpServletResponse} * Contract for writing headers to a {@link HttpServletResponse}
* *
* @see HeadersFilter * @see HeaderWriterFilter
* *
* @author Marten Deinum * @author Marten Deinum
* @author Rob Winch * @author Rob Winch

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.security.web.headers; package org.springframework.security.web.header;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
@ -33,7 +33,7 @@ import java.util.*;
* @since 3.2 * @since 3.2
* *
*/ */
public class HeadersFilter extends OncePerRequestFilter { public class HeaderWriterFilter extends OncePerRequestFilter {
/** Collection of {@link HeaderWriter} instances to write out the headers to the response . */ /** Collection of {@link HeaderWriter} instances to write out the headers to the response . */
private final List<HeaderWriter> headerWriters; private final List<HeaderWriter> headerWriters;
@ -43,7 +43,7 @@ public class HeadersFilter extends OncePerRequestFilter {
* *
* @param headerWriters the {@link HeaderWriter} instances to write out headers to the {@link HttpServletResponse}. * @param headerWriters the {@link HeaderWriter} instances to write out headers to the {@link HttpServletResponse}.
*/ */
public HeadersFilter(List<HeaderWriter> headerWriters) { public HeaderWriterFilter(List<HeaderWriter> headerWriters) {
Assert.notEmpty(headerWriters, "headerWriters cannot be null"); Assert.notEmpty(headerWriters, "headerWriters cannot be null");
this.headerWriters = headerWriters; this.headerWriters = headerWriters;
} }

View File

@ -0,0 +1,49 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.header.writers;
import java.util.ArrayList;
import java.util.List;
import org.springframework.security.web.header.Header;
/**
* A {@link StaticHeadersWriter} that inserts headers to prevent caching.
* Specifically it adds the following headers:
* <ul>
* <li>Cache-Control: no-cache, no-store, max-age=0, must-revalidate</li>
* <li>Pragma: no-cache</li>
* </ul>
*
* @author Rob Winch
* @since 3.2
*/
public final class CacheControlHeadersWriter extends StaticHeadersWriter {
/**
* Creates a new instance
*/
public CacheControlHeadersWriter() {
super(createHeaders());
}
private static List<Header> createHeaders() {
List<Header> headers = new ArrayList<Header>(2);
headers.add(new Header("Cache-Control","no-cache","no-store","max-age=0","must-revalidate"));
headers.add(new Header("Pragma","no-cache"));
return headers;
}
}

View File

@ -13,11 +13,12 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.security.web.headers; package org.springframework.security.web.header.writers;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.springframework.security.web.header.HeaderWriter;
import org.springframework.security.web.util.RequestMatcher; import org.springframework.security.web.util.RequestMatcher;
import org.springframework.util.Assert; import org.springframework.util.Assert;

View File

@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.security.web.headers; package org.springframework.security.web.header.writers;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.security.web.header.HeaderWriter;
import org.springframework.security.web.util.RequestMatcher; import org.springframework.security.web.util.RequestMatcher;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -48,11 +49,13 @@ import org.springframework.util.Assert;
* @since 3.2 * @since 3.2
*/ */
public final class HstsHeaderWriter implements HeaderWriter { public final class HstsHeaderWriter implements HeaderWriter {
private static final long DEFAULT_MAX_AGE_SECONDS = 31536000;
private static final String HSTS_HEADER_NAME = "Strict-Transport-Security"; private static final String HSTS_HEADER_NAME = "Strict-Transport-Security";
private final Log logger = LogFactory.getLog(getClass()); private final Log logger = LogFactory.getLog(getClass());
private RequestMatcher requestMatcher = new SecureRequestMatcher(); private RequestMatcher requestMatcher;
private long maxAgeInSeconds; private long maxAgeInSeconds;
@ -60,12 +63,57 @@ public final class HstsHeaderWriter implements HeaderWriter {
private String hstsHeaderValue; private String hstsHeaderValue;
public HstsHeaderWriter() { /**
this.maxAgeInSeconds = 31536000; * Creates a new instance
this.includeSubDomains = true; *
* @param requestMatcher maps to {@link #setRequestMatcher(RequestMatcher)}
* @param maxAgeInSeconds maps to {@link #setMaxAgeInSeconds(long)}
* @param includeSubDomains maps to {@link #setIncludeSubDomains(boolean)}
*/
public HstsHeaderWriter(RequestMatcher requestMatcher,
long maxAgeInSeconds, boolean includeSubDomains) {
super();
this.requestMatcher = requestMatcher;
this.maxAgeInSeconds = maxAgeInSeconds;
this.includeSubDomains = includeSubDomains;
updateHstsHeaderValue(); updateHstsHeaderValue();
} }
/**
* Creates a new instance
*
* @param maxAgeInSeconds maps to {@link #setMaxAgeInSeconds(long)}
* @param includeSubDomains maps to {@link #setIncludeSubDomains(boolean)}
*/
public HstsHeaderWriter(long maxAgeInSeconds, boolean includeSubDomains) {
this(new SecureRequestMatcher(),maxAgeInSeconds,includeSubDomains);
}
/**
* Creates a new instance
*
* @param maxAgeInSeconds maps to {@link #setMaxAgeInSeconds(long)}
*/
public HstsHeaderWriter(long maxAgeInSeconds) {
this(new SecureRequestMatcher(),maxAgeInSeconds,true);
}
/**
* Creates a new instance
*
* @param includeSubDomains maps to {@link #setIncludeSubDomains(boolean)}
*/
public HstsHeaderWriter(boolean includeSubDomains) {
this(new SecureRequestMatcher(),DEFAULT_MAX_AGE_SECONDS,includeSubDomains);
}
/**
* Creates a new instance
*/
public HstsHeaderWriter() {
this(DEFAULT_MAX_AGE_SECONDS);
}
/* /*
* (non-Javadoc) * (non-Javadoc)
* *

View File

@ -1,4 +1,4 @@
package org.springframework.security.web.headers; package org.springframework.security.web.header.writers;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -6,6 +6,8 @@ import java.util.List;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.springframework.security.web.header.Header;
import org.springframework.security.web.header.HeaderWriter;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**

View File

@ -0,0 +1,37 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.header.writers;
/**
* A {@link StaticHeadersWriter} that inserts headers to prevent content
* sniffing. Specifically the following headers are set:
* <ul>
* <li>X-Content-Type-Options: nosniff</li>
* </ul>
*
* @author Rob Winch
* @since 3.2
*/
public final class XContentTypeOptionsHeaderWriter extends StaticHeadersWriter {
/**
* Creates a new instance
*/
public XContentTypeOptionsHeaderWriter() {
super("X-Content-Type-Options","nosniff");
}
}

View File

@ -0,0 +1,118 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.header.writers;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.web.header.HeaderWriter;
/**
* Renders the <a href=
* "http://blogs.msdn.com/b/ieinternals/archive/2011/01/31/controlling-the-internet-explorer-xss-filter-with-the-x-xss-protection-http-header.aspx"
* >X-XSS-Protection header</a>.
*
* @author Rob Winch
* @since 3.2
*/
public final class XXssProtectionHeaderWriter implements HeaderWriter {
private static final String XSS_PROTECTION_HEADER = "X-XSS-Protection";
private boolean enabled;
private boolean block;
private String headerValue;
/**
* Create a new instance
*/
public XXssProtectionHeaderWriter() {
this.enabled = true;
this.block = true;
updateHeaderValue();
}
@Override
public void writeHeaders(HttpServletRequest request,
HttpServletResponse response) {
response.setHeader(XSS_PROTECTION_HEADER, headerValue);
}
/**
* If true, will contain a value of 1. For example:
*
* <pre>
* X-XSS-Protection: 1
* </pre>
*
* or if {@link #setBlock(boolean)} is true
*
*
* <pre>
* X-XSS-Protection: 1; mode=block
* </pre>
*
* If false, will explicitly disable specify that X-XSS-Protection is
* disabled. For example:
*
* <pre>
* X-XSS-Protection: 0
* </pre>
*
* @param enabled the new value
*/
public void setEnabled(boolean enabled) {
if(!enabled) {
setBlock(false);
}
this.enabled = enabled;
updateHeaderValue();
}
/**
* If false, will not specify the mode as blocked. In this instance, any
* content will be attempted to be fixed. If true, the content will be
* replaced with "#".
*
* @param enabled
* the new value
*/
public void setBlock(boolean block) {
if(!enabled && block) {
throw new IllegalArgumentException("Cannot set block to true with enabled false");
}
this.block = block;
updateHeaderValue();
}
private void updateHeaderValue() {
if(!enabled) {
this.headerValue = "0";
return;
}
this.headerValue = "1";
if(block) {
this.headerValue += "; mode=block";
}
}
@Override
public String toString() {
return getClass().getName() + " [headerValue=" + headerValue + "]";
}
}

View File

@ -1,4 +1,4 @@
package org.springframework.security.web.headers.frameoptions; package org.springframework.security.web.header.writers.frameoptions;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;

View File

@ -1,4 +1,4 @@
package org.springframework.security.web.headers.frameoptions; package org.springframework.security.web.header.writers.frameoptions;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;

View File

@ -1,4 +1,4 @@
package org.springframework.security.web.headers.frameoptions; package org.springframework.security.web.header.writers.frameoptions;
import org.springframework.util.Assert; import org.springframework.util.Assert;

View File

@ -1,4 +1,4 @@
package org.springframework.security.web.headers.frameoptions; package org.springframework.security.web.header.writers.frameoptions;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.net.URI; import java.net.URI;

View File

@ -1,4 +1,4 @@
package org.springframework.security.web.headers.frameoptions; package org.springframework.security.web.header.writers.frameoptions;
import java.util.Collection; import java.util.Collection;

View File

@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.security.web.headers.frameoptions; package org.springframework.security.web.header.writers.frameoptions;
import org.springframework.security.web.headers.HeaderWriter; import org.springframework.security.web.header.HeaderWriter;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -38,6 +38,13 @@ public class XFrameOptionsHeaderWriter implements HeaderWriter {
private final AllowFromStrategy allowFromStrategy; private final AllowFromStrategy allowFromStrategy;
private final XFrameOptionsMode frameOptionsMode; private final XFrameOptionsMode frameOptionsMode;
/**
* Creates an instance with {@link XFrameOptionsMode#DENY}
*/
public XFrameOptionsHeaderWriter() {
this(XFrameOptionsMode.DENY);
}
/** /**
* Creates a new instance * Creates a new instance
* *

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.security.web.headers; package org.springframework.security.web.header;
import static org.fest.assertions.Assertions.assertThat; import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@ -28,6 +28,8 @@ import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.mock.web.MockFilterChain; import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.web.header.HeaderWriter;
import org.springframework.security.web.header.HeaderWriterFilter;
/** /**
* Tests for the {@code HeadersFilter} * Tests for the {@code HeadersFilter}
@ -37,7 +39,7 @@ import org.springframework.mock.web.MockHttpServletResponse;
* @since 3.2 * @since 3.2
*/ */
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class HeadersFilterTests { public class HeaderWriterFilterTests {
@Mock @Mock
private HeaderWriter writer1; private HeaderWriter writer1;
@ -47,12 +49,12 @@ public class HeadersFilterTests {
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void noHeadersConfigured() throws Exception { public void noHeadersConfigured() throws Exception {
List<HeaderWriter> headerWriters = new ArrayList<HeaderWriter>(); List<HeaderWriter> headerWriters = new ArrayList<HeaderWriter>();
new HeadersFilter(headerWriters); new HeaderWriterFilter(headerWriters);
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void constructorNullWriters() throws Exception { public void constructorNullWriters() throws Exception {
new HeadersFilter(null); new HeaderWriterFilter(null);
} }
@Test @Test
@ -61,7 +63,7 @@ public class HeadersFilterTests {
headerWriters.add(writer1); headerWriters.add(writer1);
headerWriters.add(writer2); headerWriters.add(writer2);
HeadersFilter filter = new HeadersFilter(headerWriters); HeaderWriterFilter filter = new HeaderWriterFilter(headerWriters);
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse(); MockHttpServletResponse response = new MockHttpServletResponse();

View File

@ -0,0 +1,54 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.header.writers;
import static org.fest.assertions.Assertions.assertThat;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
/**
* @author Rob Winch
*
*/
public class CacheControlHeadersWriterTests {
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private CacheControlHeadersWriter writer;
@Before
public void setup() {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
writer = new CacheControlHeadersWriter();
}
@Test
public void writeHeaders() {
writer.writeHeaders(request, response);
assertThat(response.getHeaderNames().size()).isEqualTo(2);
assertThat(response.getHeaderValues("Cache-Control")).isEqualTo(Arrays.asList("no-cache","no-store","max-age=0","must-revalidate"));
assertThat(response.getHeaderValues("Pragma")).isEqualTo(Arrays.asList("no-cache"));
}
}

View File

@ -13,9 +13,8 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.security.web.headers; package org.springframework.security.web.header.writers;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -27,6 +26,7 @@ import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner; import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.web.header.HeaderWriter;
import org.springframework.security.web.util.RequestMatcher; import org.springframework.security.web.util.RequestMatcher;
/** /**

View File

@ -0,0 +1,145 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.header.writers;
import static org.fest.assertions.Assertions.assertThat;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.web.header.writers.HstsHeaderWriter;
import org.springframework.security.web.util.AnyRequestMatcher;
/**
* @author Rob Winch
*
*/
public class HstsHeaderWriterTests {
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private HstsHeaderWriter writer;
@Before
public void setup() {
request = new MockHttpServletRequest();
request.setSecure(true);
response = new MockHttpServletResponse();
writer = new HstsHeaderWriter();
}
@Test
public void allArgsCustomConstructorWriteHeaders() {
request.setSecure(false);
writer = new HstsHeaderWriter(new AnyRequestMatcher(), 15768000, false);
writer.writeHeaders(request, response);
assertThat(response.getHeaderNames().size()).isEqualTo(1);
assertThat(response.getHeader("Strict-Transport-Security")).isEqualTo("max-age=15768000");
}
@Test
public void maxAgeAndIncludeSubdomainsCustomConstructorWriteHeaders() {
request.setSecure(false);
writer = new HstsHeaderWriter(new AnyRequestMatcher(), 15768000, false);
writer.writeHeaders(request, response);
assertThat(response.getHeaderNames().size()).isEqualTo(1);
assertThat(response.getHeader("Strict-Transport-Security")).isEqualTo("max-age=15768000");
}
@Test
public void maxAgeCustomConstructorWriteHeaders() {
writer = new HstsHeaderWriter(15768000);
writer.writeHeaders(request, response);
assertThat(response.getHeaderNames().size()).isEqualTo(1);
assertThat(response.getHeader("Strict-Transport-Security")).isEqualTo("max-age=15768000 ; includeSubDomains");
}
@Test
public void includeSubDomainsCustomConstructorWriteHeaders() {
writer = new HstsHeaderWriter(false);
writer.writeHeaders(request, response);
assertThat(response.getHeaderNames().size()).isEqualTo(1);
assertThat(response.getHeader("Strict-Transport-Security")).isEqualTo("max-age=31536000");
}
@Test
public void writeHeadersDefaultValues() {
writer.writeHeaders(request, response);
assertThat(response.getHeaderNames().size()).isEqualTo(1);
assertThat(response.getHeader("Strict-Transport-Security")).isEqualTo("max-age=31536000 ; includeSubDomains");
}
@Test
public void writeHeadersIncludeSubDomainsFalse() {
writer.setIncludeSubDomains(false);
writer.writeHeaders(request, response);
assertThat(response.getHeaderNames().size()).isEqualTo(1);
assertThat(response.getHeader("Strict-Transport-Security")).isEqualTo("max-age=31536000");
}
@Test
public void writeHeadersCustomMaxAgeInSeconds() {
writer.setMaxAgeInSeconds(1);
writer.writeHeaders(request, response);
assertThat(response.getHeaderNames().size()).isEqualTo(1);
assertThat(response.getHeader("Strict-Transport-Security")).isEqualTo("max-age=1 ; includeSubDomains");
}
@Test
public void writeHeadersInsecureRequestDoesNotWriteHeader() {
request.setSecure(false);
writer.writeHeaders(request, response);
assertThat(response.getHeaderNames().isEmpty()).isTrue();
}
@Test
public void writeHeadersAnyRequestMatcher() {
writer.setRequestMatcher(new AnyRequestMatcher());
request.setSecure(false);
writer.writeHeaders(request, response);
assertThat(response.getHeaderNames().size()).isEqualTo(1);
assertThat(response.getHeader("Strict-Transport-Security")).isEqualTo("max-age=31536000 ; includeSubDomains");
}
@Test(expected = IllegalArgumentException.class)
public void setMaxAgeInSecondsToNegative() {
writer.setMaxAgeInSeconds(-1);
}
@Test(expected = IllegalArgumentException.class)
public void setRequestMatcherToNull() {
writer.setRequestMatcher(null);
}
}

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.security.web.headers; package org.springframework.security.web.header.writers;
import static org.fest.assertions.Assertions.assertThat; import static org.fest.assertions.Assertions.assertThat;
@ -24,6 +24,8 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.web.header.Header;
import org.springframework.security.web.header.writers.StaticHeadersWriter;
/** /**
* Test for the {@code StaticHeadersWriter} * Test for the {@code StaticHeadersWriter}

View File

@ -0,0 +1,53 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.header.writers;
import static org.fest.assertions.Assertions.assertThat;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
/**
* @author Rob Winch
*
*/
public class XContentTypeOptionsHeaderWriterTests {
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private XContentTypeOptionsHeaderWriter writer;
@Before
public void setup() {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
writer = new XContentTypeOptionsHeaderWriter();
}
@Test
public void writeHeaders() {
writer.writeHeaders(request, response);
assertThat(response.getHeaderNames().size()).isEqualTo(1);
assertThat(response.getHeaderValues("X-Content-Type-Options")).isEqualTo(Arrays.asList("nosniff"));
}
}

View File

@ -13,10 +13,12 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.security.web.headers; package org.springframework.security.web.header.writers;
import static org.fest.assertions.Assertions.assertThat; import static org.fest.assertions.Assertions.assertThat;
import java.util.Arrays;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletRequest;
@ -26,65 +28,66 @@ import org.springframework.mock.web.MockHttpServletResponse;
* @author Rob Winch * @author Rob Winch
* *
*/ */
public class HstsHeaderWriterTests { public class XXssProtectionHeaderWriterTests {
private MockHttpServletRequest request; private MockHttpServletRequest request;
private MockHttpServletResponse response; private MockHttpServletResponse response;
private HstsHeaderWriter writer; private XXssProtectionHeaderWriter writer;
@Before @Before
public void setup() { public void setup() {
request = new MockHttpServletRequest(); request = new MockHttpServletRequest();
request.setSecure(true);
response = new MockHttpServletResponse(); response = new MockHttpServletResponse();
writer = new XXssProtectionHeaderWriter();
writer = new HstsHeaderWriter();
} }
@Test @Test
public void writeHeadersDefaultValues() { public void writeHeaders() {
writer.writeHeaders(request, response); writer.writeHeaders(request, response);
assertThat(response.getHeaderNames().size()).isEqualTo(1); assertThat(response.getHeaderNames().size()).isEqualTo(1);
assertThat(response.getHeader("Strict-Transport-Security")).isEqualTo("max-age=31536000 ; includeSubDomains"); assertThat(response.getHeaderValues("X-XSS-Protection")).isEqualTo(Arrays.asList("1; mode=block"));
} }
@Test @Test
public void writeHeadersIncludeSubDomainsFalse() { public void writeHeadersNoBlock() {
writer.setIncludeSubDomains(false); writer.setBlock(false);
writer.writeHeaders(request, response); writer.writeHeaders(request, response);
assertThat(response.getHeaderNames().size()).isEqualTo(1); assertThat(response.getHeaderNames().size()).isEqualTo(1);
assertThat(response.getHeader("Strict-Transport-Security")).isEqualTo("max-age=31536000"); assertThat(response.getHeaderValues("X-XSS-Protection")).isEqualTo(Arrays.asList("1"));
} }
@Test @Test
public void writeHeadersCustomMaxAgeInSeconds() { public void writeHeadersDisabled() {
writer.setMaxAgeInSeconds(1); writer.setBlock(false);
writer.setEnabled(false);
writer.writeHeaders(request, response); writer.writeHeaders(request, response);
assertThat(response.getHeaderNames().size()).isEqualTo(1); assertThat(response.getHeaderNames().size()).isEqualTo(1);
assertThat(response.getHeader("Strict-Transport-Security")).isEqualTo("max-age=1 ; includeSubDomains"); assertThat(response.getHeaderValues("X-XSS-Protection")).isEqualTo(Arrays.asList("0"));
} }
@Test @Test
public void writeHeadersInsecureRequestDoesNotWriteHeader() { public void setEnabledFalseWithBlockTrue() {
request.setSecure(false); writer.setEnabled(false);
writer.writeHeaders(request, response); writer.writeHeaders(request, response);
assertThat(response.getHeaderNames().isEmpty()).isTrue(); assertThat(response.getHeaderNames().size()).isEqualTo(1);
assertThat(response.getHeaderValues("X-XSS-Protection")).isEqualTo(Arrays.asList("0"));
} }
@Test(expected = IllegalArgumentException.class)
public void setMaxAgeInSecondsToNegative() {
writer.setMaxAgeInSeconds(-1);
}
@Test(expected = IllegalArgumentException.class) @Test(expected=IllegalArgumentException.class)
public void setRequestMatcherToNull() { public void setBlockTrueWithEnabledFalse() {
writer.setRequestMatcher(null); writer.setBlock(false);
writer.setEnabled(false);
writer.setBlock(true);
} }
} }

View File

@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.security.web.headers.frameoptions; package org.springframework.security.web.header.writers.frameoptions;
import static org.fest.assertions.Assertions.assertThat; import static org.fest.assertions.Assertions.assertThat;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.web.header.writers.frameoptions.AbstractRequestParameterAllowFromStrategy;
/** /**
* @author Rob Winch * @author Rob Winch

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.security.web.headers.frameoptions; package org.springframework.security.web.header.writers.frameoptions;
import static org.fest.assertions.Assertions.assertThat; import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -27,7 +27,9 @@ import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner; import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.web.headers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode; import org.springframework.security.web.header.writers.frameoptions.AllowFromStrategy;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode;
/** /**
* @author Rob Winch * @author Rob Winch

View File

@ -1,4 +1,4 @@
package org.springframework.security.web.headers.frameoptions; package org.springframework.security.web.header.writers.frameoptions;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
@ -7,6 +7,7 @@ import java.util.regex.PatternSyntaxException;
import org.junit.Test; import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.web.header.writers.frameoptions.RegExpAllowFromStrategy;
/** /**
* *

View File

@ -1,7 +1,8 @@
package org.springframework.security.web.headers.frameoptions; package org.springframework.security.web.header.writers.frameoptions;
import org.junit.Test; import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.web.header.writers.frameoptions.StaticAllowFromStrategy;
import java.net.URI; import java.net.URI;

View File

@ -1,7 +1,8 @@
package org.springframework.security.web.headers.frameoptions; package org.springframework.security.web.header.writers.frameoptions;
import org.junit.Test; import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.web.header.writers.frameoptions.WhiteListedAllowFromStrategy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;