1. 概要

この記事では、ユーザーがにログインした後、最初に要求されたURLにユーザーをリダイレクトする方法に焦点を当てます。

以前、さまざまなタイプのユーザーに対してSpring Security でログインした後、さまざまなページにリダイレクトする方法を確認し、SpringMVCでさまざまなタイプのリダイレクトについて説明しました。

この記事は、 Spring SecurityLoginチュートリアルのトップに基づいています。

2. 一般的な方法

ログイン後にリダイレクトロジックを実装する最も一般的な方法は次のとおりです。

  • HTTPリファラーヘッダーを使用
  • 元のリクエストをセッションに保存する
  • リダイレクトされたログインURLに元のURLを追加する

HTTPリファラーヘッダーの使用は簡単な方法であり、ほとんどのブラウザーとHTTPクライアントはリファラーを自動的に設定します。 ただし、 Referer は偽造可能であり、クライアントの実装に依存しているため、 HTTPRefererヘッダーを使用してリダイレクトを実装することは一般的に推奨されていません。

セッションに元のリクエストを保存するは、この種のリダイレクトを実装するための安全で堅牢な方法です。 元のURLに加えて、元のリクエスト属性と任意のカスタムプロパティをセッションに保存できます。

そして、リダイレクトされたログインURL に元のURLを追加することは、通常、SSOの実装で見られます。 SSOサービスを介して認証されると、ユーザーは最初に要求されたページにリダイレクトされ、URLが追加されます。 追加されたURLが適切にエンコードされていることを確認する必要があります。

別の同様の実装は、ログインフォーム内の非表示フィールドに元のリクエストURLを配置することです。 しかし、これはHTTPリファラーを使用するよりも優れています。

Spring Securityでは、最初の2つのアプローチがネイティブにサポートされています。

3. AuthenticationSuccessHandler

フォームベースの認証では、リダイレクトはログインの直後に発生します。これは、 SpringSecurityAuthenticationSuccessHandlerインスタンスで処理されます。

3つのデフォルトの実装が提供されています: SimpleUrlAuthenticationSuccessHandler SavedRequestAwareAuthenticationSuccessHandler 、およびForwardAuthenticationSuccessHandler。 最初の2つの実装に焦点を当てます。

3.1。SavedRequestAwareAuthenticationSuccessHandler

SavedRequestAwareAuthenticationSuccessHandler は、セッションに保存された保存済みリクエストを利用します。 ログインに成功すると、ユーザーは元のリクエストに保存されているURLにリダイレクトされます。

フォームログインの場合、SavedRequestAwareAuthenticationSuccessHandlerがデフォルトのAuthenticationSuccessHandlerとして使用されます。

@Configuration
@EnableWebSecurity
public class RedirectionSecurityConfig extends WebSecurityConfigurerAdapter {

    //...

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
          .authorizeRequests()
          .antMatchers("/login*")
          .permitAll()
          .anyRequest()
          .authenticated()
          .and()
          .formLogin();
    }
    
}

そして、同等のXMLは次のようになります。

<http>
    <intercept-url pattern="/login" access="permitAll"/>
    <intercept-url pattern="/**" access="isAuthenticated()"/>
    <form-login />
</http>

場所「/secured」にセキュリティで保護されたリソースがあるとします。 リソースへの初めてのアクセスでは、ログインページにリダイレクトされます。 クレデンシャルを入力してログインフォームを投稿すると、最初にリクエストされたリソースの場所にリダイレクトされます。

@Test
public void givenAccessSecuredResource_whenAuthenticated_thenRedirectedBack() 
  throws Exception {
 
    MockHttpServletRequestBuilder securedResourceAccess = get("/secured");
    MvcResult unauthenticatedResult = mvc
      .perform(securedResourceAccess)
      .andExpect(status().is3xxRedirection())
      .andReturn();

    MockHttpSession session = (MockHttpSession) unauthenticatedResult
      .getRequest()
      .getSession();
    String loginUrl = unauthenticatedResult
      .getResponse()
      .getRedirectedUrl();
    mvc
      .perform(post(loginUrl)
        .param("username", userDetails.getUsername())
        .param("password", userDetails.getPassword())
        .session(session)
        .with(csrf()))
      .andExpect(status().is3xxRedirection())
      .andExpect(redirectedUrlPattern("**/secured"))
      .andReturn();

    mvc
      .perform(securedResourceAccess.session(session))
      .andExpect(status().isOk());
}

3.2. SimpleUrlAuthenticationSuccessHandler

SavedRequestAwareAuthenticationSuccessHandler と比較して、 SimpleUrlAuthenticationSuccessHandler は、リダイレクトの決定に関するより多くのオプションを提供します。

setUserReferer(true)により、リファラーベースのリダイレクトを有効にできます。

public class RefererRedirectionAuthenticationSuccessHandler 
  extends SimpleUrlAuthenticationSuccessHandler
  implements AuthenticationSuccessHandler {

    public RefererRedirectionAuthenticationSuccessHandler() {
        super();
        setUseReferer(true);
    }

}

次に、RedirectionSecurityConfigAuthenticationSuccessHandlerとして使用します。

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
      .authorizeRequests()
      .antMatchers("/login*")
      .permitAll()
      .anyRequest()
      .authenticated()
      .and()
      .formLogin()
      .successHandler(new RefererAuthenticationSuccessHandler());
}

また、XML構成の場合:

<http>
    <intercept-url pattern="/login" access="permitAll"/>
    <intercept-url pattern="/**" access="isAuthenticated()"/>
    <form-login authentication-success-handler-ref="refererHandler" />
</http>

<beans:bean 
  class="RefererRedirectionAuthenticationSuccessHandler" 
  name="refererHandler"/>

3.3. フードの下

SpringSecurityのこれらの使いやすい機能には魔法はありません。 保護されたリソースが要求されている場合、要求はさまざまなフィルターのチェーンによってフィルター処理されます。 認証プリンシパルと権限がチェックされます。 リクエストセッションがまだ認証されていない場合、AuthenticationExceptionがスローされます。

AuthenticationException は、 ExceptionTranslationFilter、でキャッチされ、認証プロセスが開始され、ログインページにリダイレクトされます。

public class ExceptionTranslationFilter extends GenericFilterBean {

    //...

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
      throws IOException, ServletException {
        //...

        handleSpringSecurityException(request, response, chain, ase);

        //...
    }

    private void handleSpringSecurityException(HttpServletRequest request,
      HttpServletResponse response, FilterChain chain, RuntimeException exception)
      throws IOException, ServletException {

        if (exception instanceof AuthenticationException) {

            sendStartAuthentication(request, response, chain,
              (AuthenticationException) exception);

        }

        //...
    }

    protected void sendStartAuthentication(HttpServletRequest request,
      HttpServletResponse response, FilterChain chain,
      AuthenticationException reason) throws ServletException, IOException {
       
       SecurityContextHolder.getContext().setAuthentication(null);
       requestCache.saveRequest(request, response);
       authenticationEntryPoint.commence(request, response, reason);
    }

    //... 

}

ログイン後、上記のようにAuthenticationSuccessHandlerの動作をカスタマイズできます。

4. 結論

このSpringSecurity の例では、ログイン後のリダイレクトの一般的な方法について説明し、SpringSecurityを使用した実装について説明しました。

検証または追加のメソッド制御が適用されていない場合、すべての実装は特定の攻撃に対して脆弱であることに注意してください。 このような攻撃により、ユーザーが悪意のあるサイトにリダイレクトされる可能性があります。

OWASP は、チートシートを提供しており、未検証のリダイレクトと転送を処理するのに役立ちます。 自分で実装を構築する必要がある場合、これは多くの助けになります。

この記事の完全な実装コードは、Githubにあります。