2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Spring Security4.1でレスポンスヘッダが正しく出力されない場合がある

Last updated at Posted at 2017-09-24

はじめに

2017/9/22現在のSpring Securityのバージョンは4.2.3のため、この問題は起こりません:sweat:

2018/9/12 追記:
Spring Security 4.2.5, 5.0.2で、再びフィルタ適用→ヘッダ書き込みの順になったので、同じ問題が発生しています。
また記事内で取り上げているissueに続報があり、APサーバによって挙動が異なるのはflush buffer sizecommit sizeの違いっぽいです。

ただ、どっちの仕様を正とするのかSpring Securityでも揺れているようなので、バージョンアップの際は注意しましょう。

確認環境

Spring Security
4.1.4.RELEASE(TERASOLUNA Server Framework for Java (5.3.0.RELEASE)を使っています)
APサーバ
JBoss EAP 7.0.0

そもそもSpring SecurityでCache-Controlヘッダを出力するには

TERASOLUNAで出力する場合ですが、spring-security.xmlにSpring Securityのheaders要素を記載します。

以下の例では、デフォルトで適用されるヘッダを無効にして、Cache-ControlX-Content-Type-Optionsヘッダだけを出力するコンポーネントのみを登録しています。

spring-security.xml
<sec:http>
	<sec:headers defaults-disabled="true">
		<sec:cache-control />
		<sec:content-type-options />
	</sec:headers>

なんで4.1だけレスポンスヘッダが出力されない場合があるの?

ヘッダ書き込み処理の手順の違い

ヘッダ書き込みをするクラスはorg.springframework.security.web.header.HeaderWriterFilterで、doFilterInternalメソッドで処理しているのですが、Spring Security 4.1.0 RC1で実装が変更されました。

関連issue:
SEC-2728: Only write cache related headers if no other cache headers found #2953

↓4.0.1.RELEASE時のdoFilterInternalメソッド

HeaderWriterFilter.java
@Override
protected void doFilterInternal(HttpServletRequest request,
	HttpServletResponse response, FilterChain filterChain)
	throws ServletException, IOException {

	for (HeaderWriter factory : headerWriters) {
		factory.writeHeaders(request, response);
	}
	filterChain.doFilter(request, response);
}

↓4.1.0 RC1時のdoFilterInternalメソッド

HeaderWriterFilter.java
	@Override
	protected void doFilterInternal(HttpServletRequest request,
			HttpServletResponse response, FilterChain filterChain)
					throws ServletException, IOException {

		HeaderWriterResponse headerWriterResponse = new HeaderWriterResponse(request,
				response, this.headerWriters);
		try {
			filterChain.doFilter(request, headerWriterResponse);
		}
		finally {
			headerWriterResponse.writeHeaders();
		}
	}

4.0.1ではヘッダ書き込み→フィルタ適用の順で処理していますが、4.1.0ではフィルタ適用→ヘッダ書き込みの順になっていますね。

なにが問題?

try-finallyを使うことで、フィルタ適用処理で例外が発生した場合もヘッダ書き込みをするようになっていますが、フィルタ適用処理でhttpストリームがフラッシュされてしまうと、ヘッダ書き込みしてもクライアント側に送られません。(バグ報告のイシューを呼んでも、具体的にどのようなケースで問題になるのかは読み解けませんでした:fearful:
以下が対象のイシューですので、詳しくはこちらを参照してください。

Spring Security not writing http headers when the http stream was flushed by a participant of the filterChain. #3975

どのような対応が必要?

Spring Securityのバージョンを上げましょう:smiley:
簡単にバージョンをあげられない場合はHeaderWriterFilterを差し替えましょう。
↑のバグ報告のイシューでHeaderWriterFilterの変更はRevertされているので、Spring Security 4.1以外のHeaderWriterFilterを使えばOKです。

HeaderWriterFilterの差し替え方

Spring Security 4.1以外のHeaderWriterFiltercom.neriudon.sample.filterに配置して、CustomizedHeaderWriterFilterと命名した場合、以下のようになります。

spring-security.xml
	<bean id="secureCacheControlHeadersWriter"
		class="org.springframework.security.web.header.writers.DelegatingRequestMatcherHeaderWriter">
		<constructor-arg>
			<bean
				class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
				<constructor-arg value="/**" />
			</bean>
		</constructor-arg>
		<constructor-arg>
			<bean
				class="org.springframework.security.web.header.writers.CacheControlHeadersWriter" />
		</constructor-arg>
	</bean>

	<bean id="customizedHeaderWriterFilter" class="com.neriudon.sample.filter.CustomizedHeaderWriterFilter">
		<constructor-arg ref="secureCacheControlHeadersWriter" />
	</bean>

	<sec:http>
		<sec:headers disabled="true" />
		<sec:custom-filter position="HEADERS_FILTER"
			ref="customizedHeaderWriterFilter" />

ポイントは

 <sec:headers disabled="true" />
        <sec:custom-filter position="HEADERS_FILTER"
            ref="customizedHeaderWriterFilter" />

の部分です。
positionを使うと独自で用意したフィルタと差し替えることが出来ますが、既存のフィルタと競合するため、headers disabled="true"としなければなりません。
そのため、差し替えるフィルタに出力するヘッダを指定しています。この部分は、TERASOLUNAのガイドラインにあるリクエストパターン毎のセキュリティヘッダの出力を引用しました。

この記事を投稿した経緯

TERASOLUNAのバージョンをあげたところ、ヘッダが出力されなくなってしまったので:frowning2:
バグ報告のイシューでもTomcat/JBossで挙動が異なるというようなコメントがありますが……奥が深いです:disappointed_relieved:

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?