AppEngine + Spring SessionでCookieが出力されない問題
AppEngine/Java + Spring Sessionを使っていて気づいたのですが、セッションの使い方によってクッキーにセッションIDが出力されない場合があります。具体的には以下の場合があります。
Spring SecurityのCSRFフィルタ:OKSpring Securityのログイン:OKControllerでHttpSessionを操作した場合:OKControllerから@SessionScope
Beanにアクセスした場合:BADControllerで@SessionAttributes
を使用した場合:BAD
10/21 追記:さらに調べたところ以下の条件になりました。
@ResponseBody
を指定したControllerがStringを返した場合:OK@ResponseBody
を指定したControllerがintを返した場合:BAD@ResponseBody
を指定したControllerがObjectを返した場合:BAD
intやObjectは、JacksonによってJSONにシリアライズされてストリームになります。すなわち、Controllerがストリームを返した場合はSpring Sessionが正しく動作しないようです。
OKは Set-Cookie: SESSION
ヘッダが出力される、BADは出力されない、です。JARを実行した場合やWARをJettyで実行した場合はすべてOKになりました。Dev Server固有の問題のようです。
スレッドダンプでSpring Sessionの動作を調べたところ、OKとBADでCookieが付加されている箇所が異なるようです。具体的には、SessionRepositoryFilter
で後続のフィルタでCookieが付与された場合はOK、そうでない場合はBADになります。
public class SessionRepositoryFilter<S extends ExpiringSession> extends OncePerRequestFilter { //(snip) try { filterChain.doFilter(strategyRequest, strategyResponse); // 後続でCookieが付与された場合はOK } finally { wrappedRequest.commitSession(); // ここでCookieが付与された場合はBAD }
FilterでHttpServletResponseを書き換えても実際のレスポンスに反映されない問題
AppEngineでは、ServletFilterで doFilter
の後にHttpServletResponseを書き換えると実際のレスポンスに反映されないようです。doFilter
の前に書き換えた場合はちゃんと反映されます。
例えば、下記の実装ではレスポンスに x-foo
ヘッダは現れません。
@Component public class ExampleFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void destroy() {} @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { chain.doFilter(request, response); // doFilterの後 val res = (HttpServletResponse) response; res.setHeader("x-foo", "bar"); } }
下記のように修正するとレスポンスに x-foo
ヘッダが出力されるようになります。
// doFilterの前 val res = (HttpServletResponse) response; res.setHeader("x-foo", "bar"); chain.doFilter(request, response);
Workaround
今のところ、以下のような対処しかなさそうです。
ログイン後にセッションにアクセスする。全リクエストで強制的にセッションを有効にする。セッションを使わない。
10/21 追記:
@ResponseBody
を指定した場合はStringを返すようにする。Jacksonを経由しないようにする。- REST APIをステートレスに設計する。