前言
- • 为了安全考虑,添加启用浏览器保护的某些头是很有用的,比如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_SrpingSecurity](https://file.cfanz.cn/uploads/png/2023/09/16/15/71YRXbdd98.png)
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;
   }
});









