前言

  • • 为了安全考虑,添加启用浏览器保护的某些头是很有用的,比如X-Frame-Options, X-XSS-Protection和X-Content-Type-Options
  • • 而HeaderWriterFilter就支持往响应头写入各种响应头

1、HeadersConfigurer

  • • HeadersConfigurer是HeaderWriterFilter对应的配置类, 是在获取HttpSecurity的时候默认开启的,也可以通过HttpSecurity.headers()手动开启

1.1 主要方法

  • • 这个配置类主要就是为了注册HeaderWriter。分为两种配置方式,一种是用户自定义,一种是官方提供的
  • • HeaderWriter:负责写入响应头的类
  • • 第一种:
public HeadersConfigurer<H> addHeaderWriter(HeaderWriter headerWriter) {
   Assert.notNull(headerWriter, "headerWriter cannot be null");
   this.headerWriters.add(headerWriter);
   return this;
}
  • • 第二种:
/**
 * 下面全是头部写入器的配置类
 * 配置类是全部都new出来的,但是里面的头部写入器可能并没有开启
 */
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();

private final PermissionsPolicyConfig permissionsPolicy = new PermissionsPolicyConfig();

1.2 构建流程

  • • HeadersConfigurer只重写了configure(...)方法:内部就是创建过滤器
@Override
public void configure(H http) {
   HeaderWriterFilter headersFilter = createHeaderWriterFilter();
   http.addFilter(headersFilter);
}
  • • HeaderWriterFilter中的头部写入器 = 用户自定义 + 默认配置类中开启的
/**
 * 获得头部写入器
 */
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;
}

/**
 * 获得所有的头部写入器
 */
private List<HeaderWriter> getHeaderWriters() {
   //添加默认头部写入器(也需要开启的)
   List<HeaderWriter> writers = new ArrayList<>();
   addIfNotNull(writers, this.contentTypeOptions.writer);
   addIfNotNull(writers, this.xssProtection.writer);
   addIfNotNull(writers, this.cacheControl.writer);
   addIfNotNull(writers, this.hsts.writer);
   addIfNotNull(writers, this.frameOptions.writer);
   addIfNotNull(writers, this.hpkp.writer);
   addIfNotNull(writers, this.contentSecurityPolicy.writer);
   addIfNotNull(writers, this.referrerPolicy.writer);
   addIfNotNull(writers, this.featurePolicy.writer);
   addIfNotNull(writers, this.permissionsPolicy.writer);

   //添加用户注册的头部写入器
   writers.addAll(this.headerWriters);
   return writers;
}

2、HeaderWriterFilter

2.1 doFilterInternal(...)

  • • 我们直接看doFilterInternal(...)方法:可以看出过滤器支持在请求的前后写入响应头
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {
   //是否在请求的开始就写头
   if (this.shouldWriteHeadersEagerly) {
      doHeadersBefore(request, response, filterChain);
   }
   else {
      doHeadersAfter(request, response, filterChain);
   }
}
  • • doHeadersBefore(...):此方法就是调用HeaderWriter写入响应头,然后执行后续的过滤器
private void doHeadersBefore(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
      throws IOException, ServletException {
   writeHeaders(request, response);
   filterChain.doFilter(request, response);
}
  • • doHeadersAfter(...):此方法稍微复杂点,这里包装了request和response
private void doHeadersAfter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
      throws IOException, ServletException {
   //将response包装为HeaderWriterResponse是为了在执行过程中就可以进行头部写入
   HeaderWriterResponse headerWriterResponse = new HeaderWriterResponse(request, response);
   HeaderWriterRequest headerWriterRequest = new HeaderWriterRequest(request, headerWriterResponse);
   try {
      filterChain.doFilter(headerWriterRequest, headerWriterResponse);
   }
   finally {
      headerWriterResponse.writeHeaders();
   }
}
  • • 我们主要看下HeaderWriterResponse的源码,主要就是重写了onResponseCommitted()方法
class HeaderWriterResponse extends OnCommittedResponseWrapper {

   private final HttpServletRequest request;

   HeaderWriterResponse(HttpServletRequest request, HttpServletResponse response) {
      super(response);
      this.request = request;
   }

   /**
    * 此方法可以直接调用
    * 比如说Controller中执行response.(include,sendError, redirect, flushBuffer)的时候,此方法就会执行
    */
   @Override
   protected void onResponseCommitted() {
      writeHeaders();
      this.disableOnResponseCommitted();
   }

   protected void writeHeaders() {
      if (isDisableOnResponseCommitted()) {
         return;
      }
      HeaderWriterFilter.this.writeHeaders(this.request, getHttpResponse());
   }

   private HttpServletResponse getHttpResponse() {
      return (HttpServletResponse) getResponse();
   }

}

2.2 shouldWriteHeadersEagerly

  • • 前面我们提到了HeaderWriter的执行顺序是通过shouldWriteHeadersEagerly这个标志位来决定,但是这个标志位不可以在HeadersConfigurer中直接配置
  • • 但是我们前面提到了所有的过滤器都有一个基于ObjectPostProcessor的回调方法,这个回调方法在HeadersConfigurer的createHeaderWriterFilter()方法中被调用

[SpringSecurity5.6.2源码分析十]:HeaderWriterFilter_响应头

image.png

  • • 我们再看postProcess(...)方法的源码:
  • • 分析可知道这是遍历所有的ObjectPostProcessor,然后看有哪些ObjectPostProcessor的泛型和传入的object一样,一样就执行回调方法
public Object postProcess(Object object) {
   for (ObjectPostProcessor opp : postProcessors) {
      Class<?> oppClass = opp.getClass();
      Class<?> oppType = GenericTypeResolver.resolveTypeArgument(oppClass,
            ObjectPostProcessor.class);
      if (oppType == null || oppType.isAssignableFrom(object.getClass())) {
         object = opp.postProcess(object);
      }
   }
   return object;
}
  • • 所以说我们可以通过以下的代码配置shouldWriteHeadersEagerly的值
http.headers().addObjectPostProcessor(new ObjectPostProcessor<HeaderWriterFilter>() {
   @Override
   public <O extends HeaderWriterFilter> O postProcess(O object) {
      HeaderWriterFilter filter = object;
      filter.setShouldWriteHeadersEagerly(true);
      return object;
   }
});