前言
- • 为了安全考虑,添加启用浏览器保护的某些头是很有用的,比如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()方法中被调用
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;
}
});