LoginSignup
3
2

Spring SecurityのfailureUrl()とfailureForwardUrl()の違い

Last updated at Posted at 2024-01-07

環境

  • Spring Boot 3.2
  • Spring Security 6.2

忙しい人のための結論

failureUrl()を使いましょう。

レスポンスが返ってこない

👇のように記述すると、ログイン画面で間違ったユーザー名・パスワードを入力した場合にレスポンスが返ってこなくなりました。

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.formLogin(login -> login
                .loginPage("/login")
                .defaultSuccessUrl("/")
                .failureForwardUrl("/login?error")  // ここが問題?
                .permitAll()
        )...

 👇のようにfailureUrl()を使うと、ログイン画面で間違ったユーザー名・パスワードを入力した場合に/login?errorにリダイレクトされました。

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.formLogin(login -> login
                .loginPage("/login")
                .defaultSuccessUrl("/")
                .failureUrl("/login?error")  // これで正しく動いた
                .permitAll()
        )...

failureForwardUrl()のソースを追う

failureForwardUrl()のソースコードを見てみると、AuthenticationFailureHandler実装としてForwardAuthenticationFailureHandlerクラスを使っていることが分かります。

FormLoginConfigurer.java(一部引用)
	public FormLoginConfigurer<H> failureForwardUrl(String forwardUrl) {
		failureHandler(new ForwardAuthenticationFailureHandler(forwardUrl));
		return this;
	}

AuthenticationFailureHandler実装は名前の通り、認証が失敗した際に呼ばれる処理です。具体的にはonAuthenticationFailure()メソッドが呼ばれます。

では、ForwardAuthenticationFailureHandlerクラスのonAuthenticationFailure()メソッドを見てみましょう。

ForwardAuthenticationFailureHandler.java(一部引用)
	@Override
	public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException exception) throws IOException, ServletException {
		request.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception);
		request.getRequestDispatcher(this.forwardUrl).forward(request, response);
	}

指定されたURLにフォワードしているだけのようですね。

このメソッドにブレークポイントを設定→IDEでデバッグ起動→ブラウザからログイン画面に間違ったユーザー名・パスワードを入力→ステップ実行すると、このメソッドが何回も何回も実行されていることが分かります。

POSTリクエストをフォワードすると、指定したURL(今回は/login?error)に同じリクエストパラメータをPOSTします。

加えて、SecurityConfigクラスで.loginProcessingUrl()を指定していないため、ユーザー名・パスワードのPOST先URLはデフォルトの/loginです。

以上のことから、次のような現象が起こっていたと考えられます。

  1. ブラウザからログイン画面に間違ったユーザー名・パスワードを入力→/loginにPOSTする
  2. ユーザー名・パスワードが間違っているので、認証が失敗しForwardAuthenticationFailureHandleronAuthenticationFailure()が実行される
  3. このメソッド内で/login?errorにフォワードする。この際、間違ったユーザー名・パスワードもPOSTされる
  4. 2と3が無限に繰り返される

failureUrl()のソースを追う

では次にfailureUrl()のソースコードを見てみましょう。AuthenticationFailureHandler実装としてSimpleUrlAuthenticationFailureHandlerクラスを使っていることが分かります。

AbstractAuthenticationFilterConfigurer.java(一部引用)
	public final T failureUrl(String authenticationFailureUrl) {
		T result = failureHandler(new SimpleUrlAuthenticationFailureHandler(authenticationFailureUrl));
		this.failureUrl = authenticationFailureUrl;
		return result;
	}

SimpleUrlAuthenticationFailureHandlerクラスのonAuthenticationFailure()メソッドはこのようになっています。

SimpleUrlAuthenticationFailureHandler.java(一部引用)
	@Override
	public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException exception) throws IOException, ServletException {
        // 中略
		if (this.forwardToDestination) {
			this.logger.debug("Forwarding to " + this.defaultFailureUrl);
			request.getRequestDispatcher(this.defaultFailureUrl).forward(request, response);
		}
		else {
			this.redirectStrategy.sendRedirect(request, response, this.defaultFailureUrl);
		}
	}

forwardToDestinationフィールドの値がtrueだったらフォワード、falseだったらリダイレクトするようです。このメソッドにブレークポイントを設定→IDEでデバッグ起動したところ、forwardToDestinationフィールドはfalseになっていました。つまり、/login?errorにリダイレクトしていました。

フォワードではなくリダイレクトなので、間違ったユーザー名・パスワードが/loginにPOSTされ続けることはありません。

結論

ユーザー名・パスワードをPOSTするURLとfailureForwardUrl()で指定したURLが同じ場合、認証失敗時にフォワードの無限ループになってしまいます。

failureUrl()は指定されたURLにリダイレクトするので、フォワードの無限ループは起こりません。

なので、基本的にはfailureUrl()を使えば間違いは無いと思います。

参考資料

3
2
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
3
2