[4] HeaderWriterFilter

本文详细剖析了HeaderWriterFilter的工作原理,介绍了其如何在请求前后写入HTTP头部信息,包括控制写入时机的关键方法shouldWriteHeadersEagerly,以及HeaderWriterFilter内部类HeaderWriterResponse的作用。同时,列举了多种HeaderWriter实例,如X-Content-Type-Options和X-XSS-Protection等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

HeaderWriterFilter

介绍

    HeaderWriterFilter是在请求前后写入一些往前请求头或者响应头写入一些信息,本身并不复杂。通过shouldWriteHeadersEagerly进行控制写入时机,shouldWriteHeadersEagerly为true时,写入操作在过滤链执行之前,否者过滤链执行完毕后再进行写入。shouldWriteHeadersEagerly默认为false,可以利用ObjectPostProcessor的机制(ObjectPostProcessor在系列文章会做讲解),设置为true。

    HeaderWriterFilter写入的HeaderWriter笔者列举了一些,具体作用可以网上查阅:

  • Header [name: X-Content-Type-Options, values: [nosniff]]
  • Header [name: X-XSS-Protection, values: [1; mode=block]]
  • Header [name: Cache-Control, values: [no-cache, no-store, max-age=0, must-revalidate]]
  • Header [name: Pragma, values: [no-cache]]
  • Header [name: Expires, values: [0]]
  • 等等

代码分析

步骤1

    HeaderWriterFilter的writeHeaders()方法写入了一组headerWriters,this.headerWriters是filter里的全局变量,this.headerWriters是由HeadersConfigurer配置的,HeaderWriterFilter和HeadersConfigurer的关键代码如下:

@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);
    }
}

private void doHeadersBefore(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
    writeHeaders(request, response);
    filterChain.doFilter(request, response);
}

private void doHeadersAfter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
    HeaderWriterResponse headerWriterResponse = new HeaderWriterResponse(request,
            response);
    HeaderWriterRequest headerWriterRequest = new HeaderWriterRequest(request,
            headerWriterResponse);
    try {
        filterChain.doFilter(headerWriterRequest, headerWriterResponse);
    } finally {
        headerWriterResponse.writeHeaders();
    }
}

void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
    for (HeaderWriter writer : this.headerWriters) {
        writer.writeHeaders(request, response);
    }
}
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, 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);
    }
}

步骤2

    HeaderWriterFilter有这样一个内部类,HeaderWriterResponse继承自OnCommittedResponseWrapper,它的onResponseCommitted()也会触发writeHeaders(),那么什么时候能回调用到onResponseCommitted(),在这个方法的代码段中打上一个断点,发现AbstractGenericHttpMessageConverter#write方法会最终触发doOnResponseCommitted(),猜测当Spring Mvc reponse的outputStream flush会触发doOnResponseCommitted(),截图和代码如下:

class HeaderWriterResponse extends OnCommittedResponseWrapper {
    private final HttpServletRequest request;

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

    @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();
    }
}

image.png
image.png

org.springframework.security.authentication.InternalAuthenticationServiceException: null at org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:123) ~[spring-security-core-5.3.4.RELEASE.jar:5.3.4.RELEASE] at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:144) ~[spring-security-core-5.3.4.RELEASE.jar:5.3.4.RELEASE] at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:199) ~[spring-security-core-5.3.4.RELEASE.jar:5.3.4.RELEASE] at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:95) ~[spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE] at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212) ~[spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE] at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE] at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE] at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE] at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE] at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.9.RELEASE.jar:5.2.9.
07-20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值