SEC-2230: HTTP Strict Transport Security (HSTS)Add support for Strict
This is a distinct filter as apposed to reusing StaticHeaderWriter since the specification specifies that the "Strict-Transport-Security" header should only be set on secure requests. It would not make sense to require DelegatingRequestMatcherHeaderWriter since this requirement is in the specification.
This commit is contained in:
parent
8013cd54d6
commit
c85328c5d1
|
@ -28,6 +28,7 @@ import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
|||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.security.web.headers.Header;
|
||||
import org.springframework.security.web.headers.HeadersFilter;
|
||||
import org.springframework.security.web.headers.HstsHeaderWriter;
|
||||
import org.springframework.security.web.headers.StaticHeadersWriter;
|
||||
import org.springframework.security.web.headers.frameoptions.AbstractRequestParameterAllowFromStrategy;
|
||||
import org.springframework.security.web.headers.frameoptions.RegExpAllowFromStrategy;
|
||||
|
@ -57,8 +58,14 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
|
|||
private static final String ATT_VALUE = "value";
|
||||
private static final String ATT_REF = "ref";
|
||||
|
||||
private static final String ATT_INCLUDE_SUBDOMAINS = "include-subdomains";
|
||||
private static final String ATT_MAX_AGE_SECONDS = "max-age-seconds";
|
||||
private static final String ATT_REQUEST_MATCHER_REF = "request-matcher-ref";
|
||||
|
||||
private static final String CACHE_CONTROL_ELEMENT = "cache-control";
|
||||
|
||||
private static final String HSTS_ELEMENT = "hsts";
|
||||
|
||||
private static final String XSS_ELEMENT = "xss-protection";
|
||||
private static final String CONTENT_TYPE_ELEMENT = "content-type-options";
|
||||
private static final String FRAME_OPTIONS_ELEMENT = "frame-options";
|
||||
|
@ -76,6 +83,7 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
|
|||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(HeadersFilter.class);
|
||||
|
||||
parseCacheControlElement(element);
|
||||
parseHstsElement(element);
|
||||
parseXssElement(element, parserContext);
|
||||
parseFrameOptionsElement(element, parserContext);
|
||||
parseContentTypeOptionsElement(element);
|
||||
|
@ -119,6 +127,26 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
|
|||
}
|
||||
}
|
||||
|
||||
private void parseHstsElement(Element element) {
|
||||
Element hstsElement = DomUtils.getChildElementByTagName(element, HSTS_ELEMENT);
|
||||
if (hstsElement != null) {
|
||||
BeanDefinitionBuilder headersWriter = BeanDefinitionBuilder.genericBeanDefinition(HstsHeaderWriter.class);
|
||||
String includeSubDomains = hstsElement.getAttribute(ATT_INCLUDE_SUBDOMAINS);
|
||||
if(StringUtils.hasText(includeSubDomains)) {
|
||||
headersWriter.addPropertyValue("includeSubDomains", includeSubDomains);
|
||||
}
|
||||
String maxAgeSeconds = hstsElement.getAttribute(ATT_MAX_AGE_SECONDS);
|
||||
if(StringUtils.hasText(maxAgeSeconds)) {
|
||||
headersWriter.addPropertyValue("maxAgeInSeconds", maxAgeSeconds);
|
||||
}
|
||||
String requestMatcherRef = hstsElement.getAttribute(ATT_REQUEST_MATCHER_REF);
|
||||
if(StringUtils.hasText(requestMatcherRef)) {
|
||||
headersWriter.addPropertyReference("requestMatcher", requestMatcherRef);
|
||||
}
|
||||
headerWriters.add(headersWriter.getBeanDefinition());
|
||||
}
|
||||
}
|
||||
|
||||
private void parseHeaderElements(Element element) {
|
||||
List<Element> headerElts = DomUtils.getChildElementsByTagName(element, GENERIC_HEADER_ELEMENT);
|
||||
for (Element headerElt : headerElts) {
|
||||
|
|
|
@ -720,7 +720,20 @@ jdbc-user-service.attlist &=
|
|||
|
||||
headers =
|
||||
## Element for configuration of the AddHeadersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.
|
||||
element headers {cache-control? & xss-protection? & frame-options? & content-type-options? & header*}
|
||||
element headers {cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & header*}
|
||||
|
||||
hsts =
|
||||
## Adds support for HTTP Strict Transport Security (HSTS)
|
||||
element hsts {hsts-options.attlist}
|
||||
hsts-options.attlist &=
|
||||
## Specifies if subdomains should be included. Default true.
|
||||
attribute include-subdomains {xsd:boolean}?
|
||||
hsts-options.attlist &=
|
||||
## Specifies the maximum ammount of time the host should be considered a Known HSTS Host. Default one year.
|
||||
attribute max-age-seconds {xsd:integer}?
|
||||
hsts-options.attlist &=
|
||||
## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.
|
||||
attribute request-matcher-ref { xsd:token }?
|
||||
|
||||
cache-control =
|
||||
## Adds Cache-Control no-cache, no-store, must-revalidate and Pragma no-cache every URL
|
||||
|
|
|
@ -2242,12 +2242,44 @@
|
|||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element ref="security:cache-control"/>
|
||||
<xs:element ref="security:xss-protection"/>
|
||||
<xs:element ref="security:hsts"/>
|
||||
<xs:element ref="security:frame-options"/>
|
||||
<xs:element ref="security:content-type-options"/>
|
||||
<xs:element ref="security:header"/>
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="hsts">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Adds support for HTTP Strict Transport Security (HSTS)
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:attributeGroup ref="security:hsts-options.attlist"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:attributeGroup name="hsts-options.attlist">
|
||||
<xs:attribute name="include-subdomains" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies if subdomains should be included. Default true.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="max-age-seconds" type="xs:integer">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the maximum ammount of time the host should be considered a Known HSTS Host.
|
||||
Default one year.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="request-matcher-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The RequestMatcher instance to be used to determine if the header should be set. Default
|
||||
is if HttpServletRequest.isSecure() is true.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
<xs:element name="cache-control">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate and Pragma no-cache every URL
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.springframework.security.web.authentication.ui.DefaultLoginPageGenera
|
|||
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;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -341,6 +342,56 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
|
|||
assertHeaders(response, ['Cache-Control': 'no-cache,no-store,max-age=0,must-revalidate','Pragma':'no-cache'])
|
||||
}
|
||||
|
||||
def 'http headers hsts'() {
|
||||
setup:
|
||||
httpAutoConfig {
|
||||
'headers'() {
|
||||
'hsts'()
|
||||
}
|
||||
}
|
||||
createAppContext()
|
||||
def springSecurityFilterChain = appContext.getBean(FilterChainProxy)
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(new MockHttpServletRequest(secure:true), response, new MockFilterChain())
|
||||
then:
|
||||
assertHeaders(response, ['Strict-Transport-Security': 'max-age=31536000 ; includeSubDomains'])
|
||||
}
|
||||
|
||||
def 'http headers hsts default only invokes on HttpServletRequest.isSecure = true'() {
|
||||
setup:
|
||||
httpAutoConfig {
|
||||
'headers'() {
|
||||
'hsts'()
|
||||
}
|
||||
}
|
||||
createAppContext()
|
||||
def springSecurityFilterChain = appContext.getBean(FilterChainProxy)
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
|
||||
then:
|
||||
response.headerNames.empty
|
||||
}
|
||||
|
||||
def 'http headers hsts custom'() {
|
||||
setup:
|
||||
httpAutoConfig {
|
||||
'headers'() {
|
||||
'hsts'('max-age-seconds':'1','include-subdomains':false, 'request-matcher-ref' : 'matcher')
|
||||
}
|
||||
}
|
||||
|
||||
xml.'b:bean'(id: 'matcher', 'class': AnyRequestMatcher.name)
|
||||
createAppContext()
|
||||
def springSecurityFilterChain = appContext.getBean(FilterChainProxy)
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
|
||||
then:
|
||||
assertHeaders(response, ['Strict-Transport-Security': 'max-age=1'])
|
||||
}
|
||||
|
||||
def assertHeaders(MockHttpServletResponse response, Map<String,String> expected) {
|
||||
assert response.headerNames == expected.keySet()
|
||||
expected.each { headerName, value ->
|
||||
|
|
|
@ -267,6 +267,9 @@
|
|||
<listitem><literal>Cache-Control</literal> and <literal>Pragma</literal> - Can be set using the
|
||||
<link xlink:href="#nsa-cache-control">cache-control</link> element. This ensures that the
|
||||
browser does not cache your secured pages.</listitem>
|
||||
<listitem><literal>Strict-Transport-Security</literal> - Can be set using the
|
||||
<link xlink:href="#nsa-hsts">hsts</link> element. This ensures that the
|
||||
browser automatically requests HTTPS for future requests.</listitem>
|
||||
<listitem><literal>X-Frame-Options</literal> - Can be set using the
|
||||
<link xlink:href="#nsa-frame-options">frame-options</link> element. The
|
||||
<link xlink:href="http://en.wikipedia.org/wiki/Clickjacking#X-Frame-Options">X-Frame-Options
|
||||
|
@ -295,6 +298,7 @@
|
|||
<listitem><link xlink:href="#nsa-content-type-options">content-type-options</link></listitem>
|
||||
<listitem><link xlink:href="#nsa-frame-options">frame-options</link></listitem>
|
||||
<listitem><link xlink:href="#nsa-header">header</link></listitem>
|
||||
<listitem><link xlink:href="#nsa-hsts">hsts</link></listitem>
|
||||
<listitem><link xlink:href="#nsa-xss-protection">xss-protection</link></listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
|
@ -310,6 +314,38 @@
|
|||
</itemizedlist>
|
||||
</section>
|
||||
</section>
|
||||
<section xml:id="nsa-hsts">
|
||||
<title><literal><hsts></literal></title>
|
||||
<para>When enabled adds the <link xlink:href="http://tools.ietf.org/html/rfc6797">Strict-Transport-Security</link> header to the response
|
||||
for any secure request. This allows the server to instruct browsers to automatically use HTTPS for future requests.</para>
|
||||
<section xml:id="nsa-hsts-attributes">
|
||||
<title><literal><hsts></literal> Attributes</title>
|
||||
<section xml:id="nsa-hsts-include-subdomains">
|
||||
<title><literal>include-sub-domains</literal></title>
|
||||
<para>
|
||||
Specifies if subdomains should be included. Default true.
|
||||
</para>
|
||||
</section>
|
||||
<section xml:id="nsa-hsts-max-age-seconds">
|
||||
<title><literal>max-age-seconds</literal></title>
|
||||
<para>
|
||||
Specifies the maximum ammount of time the host should be considered a Known HSTS Host. Default one year.
|
||||
</para>
|
||||
</section>
|
||||
<section xml:id="nsa-hsts-request-matcher-ref">
|
||||
<title><literal>request-matcher-ref</literal></title>
|
||||
<para>
|
||||
The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
<section xml:id="nsa-hsts-parents">
|
||||
<title>Parent Elements of <literal><hsts></literal></title>
|
||||
<itemizedlist>
|
||||
<listitem><link xlink:href="#nsa-headers">headers</link></listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
</section>
|
||||
<section xml:id="nsa-frame-options">
|
||||
<title><literal><frame-options></literal></title>
|
||||
<para>When enabled adds the <link xlink:href="http://tools.ietf.org/html/draft-ietf-websec-x-frame-options-01">X-Frame-Options header</link> to the response, this allows newer browsers to do some security
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* 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.headers;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.security.web.util.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Provides support for <a href="http://tools.ietf.org/html/rfc6797">HTTP Strict
|
||||
* Transport Security (HSTS)</a>.
|
||||
*
|
||||
* <p>
|
||||
* By default the expiration is one year and subdomains will be included. This
|
||||
* can be customized using {@link #setMaxAgeInSeconds(long)} and
|
||||
* {@link #setIncludeSubDomains(boolean)} respectively.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Since <a href="http://tools.ietf.org/html/rfc6797#section-7.2">section
|
||||
* 7.2</a> states that HSTS Host MUST NOT include the STS header in HTTP
|
||||
* responses, the default behavior is that the "Strict-Transport-Security" will
|
||||
* only be added when {@link HttpServletRequest#isSecure()} returns {@code true}
|
||||
* . At times this may need to be customized. For example, in some situations
|
||||
* where SSL termination is used, something else may be used to determine if SSL
|
||||
* was used. For these circumstances, {@link #setRequestMatcher(RequestMatcher)}
|
||||
* can be invoked with a custom {@link RequestMatcher}.
|
||||
* </p>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class HstsHeaderWriter implements HeaderWriter {
|
||||
private static final String HSTS_HEADER_NAME = "Strict-Transport-Security";
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private RequestMatcher requestMatcher = new SecureRequestMatcher();
|
||||
|
||||
private long maxAgeInSeconds;
|
||||
|
||||
private boolean includeSubDomains;
|
||||
|
||||
private String hstsHeaderValue;
|
||||
|
||||
public HstsHeaderWriter() {
|
||||
this.maxAgeInSeconds = 31536000;
|
||||
this.includeSubDomains = true;
|
||||
updateHstsHeaderValue();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.security.web.headers.HeaderWriter#writeHeaders(javax
|
||||
* .servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
|
||||
*/
|
||||
@Override
|
||||
public void writeHeaders(HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
if (requestMatcher.matches(request)) {
|
||||
response.setHeader(HSTS_HEADER_NAME, hstsHeaderValue);
|
||||
} else if (logger.isDebugEnabled()) {
|
||||
logger.debug("Not injecting HSTS header since it did not match the requestMatcher "
|
||||
+ requestMatcher);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link RequestMatcher} used to determine if the
|
||||
* "Strict-Transport-Security" should be added. If true the header is added,
|
||||
* else the header is not added. By default the header is added when
|
||||
* {@link HttpServletRequest#isSecure()} returns true.
|
||||
*
|
||||
* @param requestMatcher
|
||||
* the {@link RequestMatcher} to use.
|
||||
* @throws IllegalArgumentException
|
||||
* if {@link RequestMatcher} is null
|
||||
*/
|
||||
public void setRequestMatcher(RequestMatcher requestMatcher) {
|
||||
Assert.notNull(requestMatcher, "requestMatcher cannot be null");
|
||||
this.requestMatcher = requestMatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets the value (in seconds) for the max-age directive of the
|
||||
* Strict-Transport-Security header. The default is one year.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This instructs browsers how long to remember to keep this domain as a
|
||||
* known HSTS Host. See <a
|
||||
* href="http://tools.ietf.org/html/rfc6797#section-6.1.1">Section 6.1.1</a>
|
||||
* for additional details.
|
||||
* </p>
|
||||
*
|
||||
* @param maxAgeInSeconds
|
||||
* the maximum amount of time (in seconds) to consider this
|
||||
* domain as a known HSTS Host.
|
||||
* @throws IllegalArgumentException
|
||||
* if maxAgeInSeconds is negative
|
||||
*/
|
||||
public void setMaxAgeInSeconds(long maxAgeInSeconds) {
|
||||
if (maxAgeInSeconds < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"maxAgeInSeconds must be non-negative. Got "
|
||||
+ maxAgeInSeconds);
|
||||
}
|
||||
this.maxAgeInSeconds = maxAgeInSeconds;
|
||||
updateHstsHeaderValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* If true, subdomains should be considered HSTS Hosts too. The default is
|
||||
* true.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* See <a href="http://tools.ietf.org/html/rfc6797#section-6.1.2">Section
|
||||
* 6.1.2</a> for additional details.
|
||||
* </p>
|
||||
*
|
||||
* @param includeSubDomains
|
||||
* true to include subdomains, else false
|
||||
*/
|
||||
public void setIncludeSubDomains(boolean includeSubDomains) {
|
||||
this.includeSubDomains = includeSubDomains;
|
||||
updateHstsHeaderValue();
|
||||
}
|
||||
|
||||
private void updateHstsHeaderValue() {
|
||||
String headerValue = "max-age=" + maxAgeInSeconds;
|
||||
if (includeSubDomains) {
|
||||
headerValue += " ; includeSubDomains";
|
||||
}
|
||||
this.hstsHeaderValue = headerValue;
|
||||
}
|
||||
|
||||
private static final class SecureRequestMatcher implements RequestMatcher {
|
||||
@Override
|
||||
public boolean matches(HttpServletRequest request) {
|
||||
return request.isSecure();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* 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.headers;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* @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 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(expected = IllegalArgumentException.class)
|
||||
public void setMaxAgeInSecondsToNegative() {
|
||||
writer.setMaxAgeInSeconds(-1);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void setRequestMatcherToNull() {
|
||||
writer.setRequestMatcher(null);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue