Spring Bootで実装した認証付きWeb APIで異なるAPIのレスポンスが返される問題

発生していた問題

  1. 未ログイン状態で、認証が必要なWebAPI-1にリクエス
    • HTTP 401が返される
  2. ログイン
    • HTTP 200 ログイン成功
    • レスポンスボディなし
  3. WebAPI-2(1とは異なるAPI)を呼び出す
    • WebAPI-1のレスポンスが返される
    • クライアントサイドのリクエストパラメータは、WebAPI-2を指定
    • サーバーログを見る限り、1.でリクエストした際のリクエストパラメータを元に処理が行われ、レスポンスが返されている

Spring Bootのバージョン

  • Spring Boot 1.4.2
  • Spring Security 4.1.3

原因

問題が発生するパターンのSpring Securityの設定は概ねこんな感じ。

public class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter {
    private static final String URL_PATTERN = "xxx";

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        http.regexMatcher(URL_PATTERN)
                .formLogin()
                .successHandler((req, res, auth) -> res.setStatus(HttpServletResponse.SC_OK))
                .failureHandler((req, res, auth) -> res.setStatus(HttpServletResponse.SC_UNAUTHORIZED))
                .permitAll();

        http.regexMatcher(URL_PATTERN)
                .exceptionHandling()
                .authenticationEntryPoint(new Http401AuthenticationEntryPoint("Bearer error=\"invalid_request\""));

        http.regexMatcher(URL_PATTERN).csrf().disable();
    }
}

また、APIは下記のような実装を行っていた。

  • 単一URLにPOST
  • HTTP Headerによって処理をルーティング

medium.com

この環境下で、前述の再現手順を行うと、以下のようなことが発生していた。

対応策

Spring Securityの設定でリクエストキャッシュを無効にする。

public class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter {
    private static final String URL_PATTERN = "xxx";

    @Override
    protected void configure(final HttpSecurity http) throws Exception {

        http.regexMatcher(URL_PATTERN)
                .requestCache().requestCache(new NullRequestCache());

    }
}

参考URL

http://www.sedooe.com/2016/04/rest-authentication-using-spring-security-and-spring-session/