作为一个配置HttpSecurity
的SecurityConfigurer
,HeadersConfigurer
的配置任务如下 :
Filter
HeaderWriterFilter
HeadersConfigurer
自己内置定义了一组特定头部的配置类并允许使用者配置,这些配置类每个其实对应一个相应的头部写入器HeaderWriter
。除此之外,HeadersConfigurer
也允许开发者提供自定义的头部写入器HeaderWriter
。
HeadersConfigurer
自己内置的特定头部的配置类如下 :
配置类 | 对应头部写入器 | 对应头部 |
---|---|---|
CacheControlConfig |
CacheControlHeadersWriter |
Expires,Pragma ,Cache-Control |
ContentSecurityPolicyConfig |
ContentSecurityPolicyHeaderWriter |
Content-Security-Policy ,Content-Security-Policy-Report-Only |
ContentTypeOptionsConfig |
XContentTypeOptionsHeaderWriter |
X-Content-Type-Options |
FeaturePolicyConfig |
FeaturePolicyHeaderWriter |
Feature-Policy |
FrameOptionsConfig |
XFrameOptionsHeaderWriter |
X-Frame-Options |
HpkpConfig |
HpkpHeaderWriter |
Public-Key-Pins ,Public-Key-Pins-Report-Only |
HstsConfig |
HstsHeaderWriter |
Strict-Transport-Security |
ReferrerPolicyConfig |
ReferrerPolicyHeaderWriter |
Referrer-Policy |
XXssConfig |
XXssProtectionHeaderWriter |
X-XSS-Protection |
HeadersConfigurer
通过如下方法允许开发人员添加自定义头部写入器 :
addHeaderWriter(HeaderWriter headerWriter)
应用HeadersConfigurer
而不做任何自定义的缺省情况下,它所产生的HeaderWriterFilter
会往响应写入如下头部信息 :
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
// HttpSecurity 代码片段
public HeadersConfigurer<HttpSecurity> headers() throws Exception {
return getOrApply(new HeadersConfigurer<>());
}
源代码版本 Spring Security Config 5.1.4.RELEASE
package org.springframework.security.config.annotation.web.configurers;
// 省略 imports
public class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractHttpConfigurer<HeadersConfigurer<H>, H> {
private List<HeaderWriter> headerWriters = new ArrayList<>();
// --- default header writers ---
private final ContentTypeOptionsConfig contentTypeOptions = new ContentTypeOptionsConfig();
private final XXssConfig xssProtection = new XXssConfig();
private final CacheControlConfig cacheControl = new CacheControlConfig();
private final HstsConfig hsts = new HstsConfig();
private final FrameOptionsConfig frameOptions = new FrameOptionsConfig();
private final HpkpConfig hpkp = new HpkpConfig();
private final ContentSecurityPolicyConfig contentSecurityPolicy =
new ContentSecurityPolicyConfig();
private final ReferrerPolicyConfig referrerPolicy = new ReferrerPolicyConfig();
private final FeaturePolicyConfig featurePolicy = new FeaturePolicyConfig();
/**
* 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;
}
/**
* Configures the {@link XContentTypeOptionsHeaderWriter} which inserts the X-Content-Type-Options:
*
*
* X-Content-Type-Options: nosniff
*
*
* @return the ContentTypeOptionsConfig for additional customizations
*/
public ContentTypeOptionsConfig contentTypeOptions() {
return contentTypeOptions.enable();
}
public final class ContentTypeOptionsConfig {
private XContentTypeOptionsHeaderWriter writer;
private ContentTypeOptionsConfig() {
enable();
}
/**
* Removes the X-XSS-Protection header.
*
* @return {@link HeadersConfigurer} for additional customization.
*/
public HeadersConfigurer<H> disable() {
writer = null;
return and();
}
/**
* Allows customizing the {@link HeadersConfigurer}
* @return the {@link HeadersConfigurer} for additional customization
*/
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
/**
* Ensures that Content Type Options is enabled
*
* @return the {@link ContentTypeOptionsConfig} for additional customization
*/
private ContentTypeOptionsConfig enable() {
if (writer == null) {
writer = new XContentTypeOptionsHeaderWriter();
}
return this;
}
}
/**
* Note this is not comprehensive XSS protection!
*
*
* Allows customizing the XXssProtectionHeaderWriter which adds the X-XSS-Protection header
*
*
* @return the HeadersConfigurer for additional customizations
*/
public XXssConfig xssProtection() {
return xssProtection.enable();
}
public final class XXssConfig {
private XXssProtectionHeaderWriter writer;
private XXssConfig() {
enable();
}
/**
* 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 XXssConfig block(boolean enabled) {
writer.setBlock(enabled);
return this;
}
/**
* If true, the header value will contain a value of 1. For example:
*
*
* X-XSS-Protection: 1
*
*
* or if {@link XXssProtectionHeaderWriter#setBlock(boolean)} of the given
* XXssProtectionHeaderWriter is true
*
*
*
* X-XSS-Protection: 1; mode=block
*
*
* If false, will explicitly disable specify that X-XSS-Protection is disabled.
* For example:
*
*
* X-XSS-Protection: 0
*
*
* @param enabled the new value
*/
public XXssConfig xssProtectionEnabled(boolean enabled) {
writer.setEnabled(enabled);
return this;
}
/**
* Disables X-XSS-Protection header (does not include it)
*
* @return the {@link HeadersConfigurer} for additional configuration
*/
public HeadersConfigurer<H> disable() {
writer = null;
return and();
}
/**
* Allows completing configuration of X-XSS-Protection and continuing
* configuration of headers.
*
* @return the {@link HeadersConfigurer} for additional configuration
*/
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
/**
* Ensures the X-XSS-Protection header is enabled if it is not already.
*
* @return the {@link XXssConfig} for additional customization
*/
private XXssConfig enable() {
if (writer == null) {
writer = new XXssProtectionHeaderWriter();
}
return this;
}
}
/**
* Allows customizing the {@link CacheControlHeadersWriter}. Specifically it adds the
* following headers:
*
* - Cache-Control: no-cache, no-store, max-age=0, must-revalidate
* - Pragma: no-cache
* - Expires: 0
*
*
* @return the {@link HeadersConfigurer} for additional customizations
*/
public CacheControlConfig cacheControl() {
return cacheControl.enable();
}
public final class CacheControlConfig {
private CacheControlHeadersWriter writer;
private CacheControlConfig() {
enable();
}
/**
* Disables Cache Control
*
* @return the {@link HeadersConfigurer} for additional configuration
*/
public HeadersConfigurer<H> disable() {
writer = null;
return HeadersConfigurer.this;
}
/**
* Allows completing configuration of Cache Control and continuing
* configuration of headers.
*
* @return the {@link HeadersConfigurer} for additional configuration
*/
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
/**
* Ensures the Cache Control headers are enabled if they are not already.
*
* @return the {@link CacheControlConfig} for additional customization
*/
private CacheControlConfig enable() {
if (writer == null) {
writer = new CacheControlHeadersWriter();
}
return this;
}
}
/**
* Allows customizing the {@link HstsHeaderWriter} which provides support for HTTP Strict Transport Security
* (HSTS).
*
* @return the {@link HeadersConfigurer} for additional customizations
*/
public HstsConfig httpStrictTransportSecurity() {
return hsts.enable();
}
public final class HstsConfig {
private HstsHeaderWriter writer;
private HstsConfig() {
enable();
}
/**
*
* Sets the value (in seconds) for the max-age directive of the
* Strict-Transport-Security header. The default is one year.
*
*
*
* This instructs browsers how long to remember to keep this domain as a known
* HSTS Host. See Section 6.1.1 for
* additional details.
*
*
* @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 HstsConfig maxAgeInSeconds(long maxAgeInSeconds) {
writer.setMaxAgeInSeconds(maxAgeInSeconds);
return this;
}
/**
* 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 HstsConfig requestMatcher(RequestMatcher requestMatcher) {
writer.setRequestMatcher(requestMatcher);
return this;
}
/**
*
* If true, subdomains should be considered HSTS Hosts too. The default is true.
*
*
*
* See Section
* 6.1.2 for additional details.
*
*
* @param includeSubDomains true to include subdomains, else false
*/
public HstsConfig includeSubDomains(boolean includeSubDomains) {
writer.setIncludeSubDomains(includeSubDomains);
return this;
}
/**
* Disables Strict Transport Security
*
* @return the {@link HeadersConfigurer} for additional configuration
*/
public HeadersConfigurer<H> disable() {
writer = null;
return HeadersConfigurer.this;
}
/**
* Allows completing configuration of Strict Transport Security and continuing
* configuration of headers.
*
* @return the {@link HeadersConfigurer} for additional configuration
*/
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
/**
* Ensures that Strict-Transport-Security is enabled if it is not already
*
* @return the {@link HstsConfig} for additional customization
*/
private HstsConfig enable() {
if (writer == null) {
writer = new HstsHeaderWriter();
}
return this;
}
}
/**
* Allows customizing the {@link XFrameOptionsHeaderWriter}.
*
* @return the {@link HeadersConfigurer} for additional customizations
*/
public FrameOptionsConfig frameOptions() {
return frameOptions.enable();
}
public final class FrameOptionsConfig {
private XFrameOptionsHeaderWriter writer;
private FrameOptionsConfig() {
enable();
}
/**
* Specify to DENY framing any content from this application.
*
* @return the {@link HeadersConfigurer} for additional customization.
*/
public HeadersConfigurer<H> deny() {
writer = new XFrameOptionsHeaderWriter(XFrameOptionsMode.DENY);
return and();
}
/**
*
* Specify to allow any request that comes from the same origin to frame this
* application. For example, if the application was hosted on example.com, then
* example.com could frame the application, but evil.com could not frame the
* application.
*
*
* @return the {@link HeadersConfigurer} for additional customization.
*/
public HeadersConfigurer<H> sameOrigin() {
writer = new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN);
return and();
}
/**
* Prevents the header from being added to the response.
*
* @return the {@link HeadersConfigurer} for additional configuration.
*/
public HeadersConfigurer<H> disable() {
writer = null;
return and();
}
/**
* Allows continuing customizing the headers configuration.
*
* @return the {@link HeadersConfigurer} for additional configuration
*/
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
/**
* Enables FrameOptionsConfig if it is not already enabled.
*
* @return the FrameOptionsConfig for additional customization.
*/
private FrameOptionsConfig enable() {
if (writer == null) {
writer = new XFrameOptionsHeaderWriter(XFrameOptionsMode.DENY);
}
return this;
}
}
/**
* Allows customizing the {@link HpkpHeaderWriter} which provides support for HTTP Public Key Pinning (HPKP).
*
* @return the {@link HeadersConfigurer} for additional customizations
*
* @since 4.1
*/
public HpkpConfig httpPublicKeyPinning() {
return hpkp.enable();
}
public final class HpkpConfig {
private HpkpHeaderWriter writer;
private HpkpConfig() {}
/**
*
* Sets the value for the pin- directive of the Public-Key-Pins header.
*
*
*
* The pin directive specifies a way for web host operators to indicate
* a cryptographic identity that should be bound to a given web host.
* See Section 2.1.1
* for additional details.
*
*
* @param pins the map of base64-encoded SPKI fingerprint & cryptographic
* hash algorithm pairs.
* @throws IllegalArgumentException if pins is null
*/
public HpkpConfig withPins(Map<String, String> pins) {
writer.setPins(pins);
return this;
}
/**
*
* Adds a list of SHA256 hashed pins for the pin- directive of the Public-Key-Pins header.
*
*
*
* The pin directive specifies a way for web host operators to indicate
* a cryptographic identity that should be bound to a given web host.
* See Section 2.1.1
* for additional details.
*
*
* @param pins a list of base64-encoded SPKI fingerprints.
* @throws IllegalArgumentException if a pin is null
*/
public HpkpConfig addSha256Pins(String ... pins) {
writer.addSha256Pins(pins);
return this;
}
/**
*
* Sets the value (in seconds) for the max-age directive of the Public-Key-Pins header.
* The default is 60 days.
*
*
*
* This instructs browsers how long they should regard the host (from whom the
* message was received)
* as a known pinned host. See
* Section 2.1.2
* for additional details.
*
*
* @param maxAgeInSeconds the maximum amount of time (in seconds) to regard the host
* as a known pinned host.
* @throws IllegalArgumentException if maxAgeInSeconds is negative
*/
public HpkpConfig maxAgeInSeconds(long maxAgeInSeconds) {
writer.setMaxAgeInSeconds(maxAgeInSeconds);
return this;
}
/**
*
* If true, the pinning policy applies to this pinned host as well as any subdomains
* of the host's domain name. The default is false.
*
*
*
* See Section 2.1.3
* for additional details.
*
*
* @param includeSubDomains true to include subdomains, else false
*/
public HpkpConfig includeSubDomains(boolean includeSubDomains) {
writer.setIncludeSubDomains(includeSubDomains);
return this;
}
/**
*
* If true, the browser should not terminate the connection with the server.
* The default is true.
*
*
*
* See Section 2.1
* for additional details.
*
*
* @param reportOnly true to report only, else false
*/
public HpkpConfig reportOnly(boolean reportOnly) {
writer.setReportOnly(reportOnly);
return this;
}
/**
*
* Sets the URI to which the browser should report pin validation failures.
*
*
*
* See Section 2.1.4
* for additional details.
*
*
* @param reportUri the URI where the browser should send the report to.
*/
public HpkpConfig reportUri(URI reportUri) {
writer.setReportUri(reportUri);
return this;
}
/**
*
* Sets the URI to which the browser should report pin validation failures.
*
*
*
* See Section 2.1.4
* for additional details.
*
*
* @param reportUri the URI where the browser should send the report to.
* @throws IllegalArgumentException if the reportUri is not a valid URI
*/
public HpkpConfig reportUri(String reportUri) {
writer.setReportUri(reportUri);
return this;
}
/**
* Prevents the header from being added to the response.
*
* @return the {@link HeadersConfigurer} for additional configuration.
*/
public HeadersConfigurer<H> disable() {
writer = null;
return and();
}
/**
* Allows completing configuration of Public Key Pinning and continuing
* configuration of headers.
*
* @return the {@link HeadersConfigurer} for additional configuration
*/
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
/**
* Ensures that Public-Key-Pins or Public-Key-Pins-Report-Only is enabled
* if it is not already
*
* @return the {@link HstsConfig} for additional customization
*/
private HpkpConfig enable() {
if (writer == null) {
writer = new HpkpHeaderWriter();
}
return this;
}
}
/**
*
* Allows configuration for Content Security Policy (CSP)
* Level 2.
*
*
*
* Calling this method automatically enables (includes) the Content-Security-Policy header
* in the response using the supplied security policy directive(s).
*
*
*
* Configuration is provided to the {@link ContentSecurityPolicyHeaderWriter} which supports
* the writing of the two headers as detailed in the W3C Candidate Recommendation:
*
*
* - Content-Security-Policy
* - Content-Security-Policy-Report-Only
*
*
* @see ContentSecurityPolicyHeaderWriter
* @since 4.1
* @return the ContentSecurityPolicyConfig for additional configuration
* @throws IllegalArgumentException if policyDirectives is null or empty
*/
public ContentSecurityPolicyConfig contentSecurityPolicy(String policyDirectives) {
this.contentSecurityPolicy.writer =
new ContentSecurityPolicyHeaderWriter(policyDirectives);
return contentSecurityPolicy;
}
public final class ContentSecurityPolicyConfig {
private ContentSecurityPolicyHeaderWriter writer;
private ContentSecurityPolicyConfig() {
}
/**
* Enables (includes) the Content-Security-Policy-Report-Only header in the response.
*
* @return the {@link ContentSecurityPolicyConfig} for additional configuration
*/
public ContentSecurityPolicyConfig reportOnly() {
this.writer.setReportOnly(true);
return this;
}
/**
* Allows completing configuration of Content Security Policy and continuing
* configuration of headers.
*
* @return the {@link HeadersConfigurer} for additional configuration
*/
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
}
/**
* Clears all of the default headers from the response. After doing so, one can add
* headers back. For example, if you only want to use Spring Security's cache control
* you can use the following:
*
*
* http.headers().defaultsDisabled().cacheControl();
*
*
* @return the {@link HeadersConfigurer} for additional customization
*/
public HeadersConfigurer<H> defaultsDisabled() {
contentTypeOptions.disable();
xssProtection.disable();
cacheControl.disable();
hsts.disable();
frameOptions.disable();
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() {
List<HeaderWriter> writers = getHeaderWriters();
if (writers.isEmpty()) {
throw new IllegalStateException(
"Headers security is enabled, but no headers will be added. "
+ " Either add headers or disable headers security");
}
HeaderWriterFilter headersFilter = new HeaderWriterFilter(writers);
headersFilter = postProcess(headersFilter);
return headersFilter;
}
/**
* Gets the {@link HeaderWriter} instances and possibly initializes with the defaults.
*
* @return
*/
private List<HeaderWriter> getHeaderWriters() {
List<HeaderWriter> writers = new ArrayList<>();
addIfNotNull(writers, contentTypeOptions.writer);
addIfNotNull(writers, xssProtection.writer);
addIfNotNull(writers, cacheControl.writer);
addIfNotNull(writers, hsts.writer);
addIfNotNull(writers, frameOptions.writer);
addIfNotNull(writers, hpkp.writer);
addIfNotNull(writers, contentSecurityPolicy.writer);
addIfNotNull(writers, referrerPolicy.writer);
addIfNotNull(writers, featurePolicy.writer);
writers.addAll(headerWriters);
return writers;
}
private <T> void addIfNotNull(List<T> values, T value) {
if (value != null) {
values.add(value);
}
}
/**
*
* Allows configuration for Referrer Policy.
*
*
*
* Configuration is provided to the {@link ReferrerPolicyHeaderWriter} which support the writing
* of the header as detailed in the W3C Technical Report:
*
*
* - Referrer-Policy
*
*
* Default value is:
*
*
* Referrer-Policy: no-referrer
*
*
* @see ReferrerPolicyHeaderWriter
* @since 4.2
* @return the ReferrerPolicyConfig for additional configuration
*/
public ReferrerPolicyConfig referrerPolicy() {
this.referrerPolicy.writer = new ReferrerPolicyHeaderWriter();
return this.referrerPolicy;
}
/**
*
* Allows configuration for Referrer Policy.
*
*
*
* Configuration is provided to the {@link ReferrerPolicyHeaderWriter} which support the writing
* of the header as detailed in the W3C Technical Report:
*
*
* - Referrer-Policy
*
*
* @see ReferrerPolicyHeaderWriter
* @since 4.2
* @return the ReferrerPolicyConfig for additional configuration
* @throws IllegalArgumentException if policy is null or empty
*/
public ReferrerPolicyConfig referrerPolicy(ReferrerPolicy policy) {
this.referrerPolicy.writer = new ReferrerPolicyHeaderWriter(policy);
return this.referrerPolicy;
}
public final class ReferrerPolicyConfig {
private ReferrerPolicyHeaderWriter writer;
private ReferrerPolicyConfig() {
}
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
}
/**
* Allows configuration for Feature
* Policy.
*
* Calling this method automatically enables (includes) the {@code Feature-Policy}
* header in the response using the supplied policy directive(s).
*
* Configuration is provided to the {@link FeaturePolicyHeaderWriter} which is
* responsible for writing the header.
*
* @see FeaturePolicyHeaderWriter
* @since 5.1
* @return the {@link FeaturePolicyHeaderWriter} for additional configuration
* @throws IllegalArgumentException if policyDirectives is {@code null} or empty
*/
public FeaturePolicyConfig featurePolicy(String policyDirectives) {
this.featurePolicy.writer = new FeaturePolicyHeaderWriter(policyDirectives);
return featurePolicy;
}
public final class FeaturePolicyConfig {
private FeaturePolicyHeaderWriter writer;
private FeaturePolicyConfig() {
}
/**
* Allows completing configuration of Feature Policy and continuing configuration
* of headers.
*
* @return the {@link HeadersConfigurer} for additional configuration
*/
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
}
}