Spring Security - ログイン後に前のURLにリダイレクトする

1概要

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

これまで、さまざまな種類のユーザーに対して Spring Security__でログインした後にさまざまなページにリダイレクトする方法 を見てきました。

この記事はリンクのトップに基づいています:/spring-security-login[Spring Securityログイン]チュートリアル。

2一般的な方法

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

  • HTTP Refererヘッダを使う

  • 元のリクエストをセッションに保存する

  • リダイレクトされたログインURLにオリジナルのURLを追加する

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

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

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

別の同様の実装は、ログインフォーム内の隠しフィールドに元のリクエストURLを入れることです。しかし、これは HTTP Referer を使用することに勝るものではありません。

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

3 AuthenticationSuccessHandler

フォームベース認証では、リダイレクトはログイン直後に行われ、https://github.com/spring-projects/spring-security/blob/master/web/src/main/java/org/springframework/securityで処理されます Spring Security の/web/authentication/AuthenticationSuccessHandler.java[ AuthenticationSuccessHandler ]インスタンス。

3つのデフォルト実装が提供されています。

SimpleUrlAuthenticationSuccessHandler ghttps//.com/spring-projects/spring-security/blob/master/web/src/main/java/org/springframework/security/web/authentication/SavedRequestAwareAuthenticationSuccessHandler.java[ SavedRequestAwareAuthenticationSuccessHandler ]およびhttps://github.com/spring- projects/spring-security/blob/master/web/src/main/java/org/springframework/security/web/authentication/ForwardAuthenticationSuccessHandler.java[ 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>

セキュリティ保護されたリソースが "/secure"の場所にあるとします。初めてリソースにアクセスすると、ログインページにリダイレクトされます。資格情報を入力してログインフォームを送信すると、最初にリクエストされたリソースの場所にリダイレクトされます。

@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) によって、Refererベースのリダイレクトを有効にすることができます。

public class RefererRedirectionAuthenticationSuccessHandler
  extends SimpleUrlAuthenticationSuccessHandler
  implements AuthenticationSuccessHandler {

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

}

それを RedirectionSecurityConfig AuthenticationSuccessHandler として使用します。

@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. フードの下

Spring Security のこれらの使いやすい機能に魔法はありません。

保護されたリソースが要求されているとき、その要求はさまざまなフィルタのチェーンによってフィルタ処理されます。認証主体と権限がチェックされます。リクエストセッションがまだ認証されていない場合は、https://github.com/spring-projects/spring-security/blob/master/core/src/main/java/org/springframework/security/core/認証認証例外。]がスローされます。

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

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結論

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

  • 検証や追加のメソッド制御が適用されていない場合、ここで説明したすべての実装は特定の攻撃に対して脆弱です。

このような攻撃により、ユーザーが悪質なサイトにリダイレクトされる可能性があります。

OWASP は、未検証のリダイレクトや転送を処理するためにhttps://www.owasp.org/index.php/Unvalidated Redirects and Forwards Cheat__Sheet[簡易シート]を提供しています。私たちが自分自身で実装を構築する必要があるならば、これは大いに役立つでしょう。

この記事の完全な実装コードはhttps://github.com/eugenp/tutorials/tree/master/spring-security-mvc-login[over Github]です。