カジュアルな技術ノート

小難しい技術のお話をできるだけわかりやすく...

Spring Security をわかりやすく(認証全体像編・その2)

何回かに渡って Spring Security の認証を扱っていきたいと思います。
Spring Security を触ったことのある人にまず聞いてみたいのですが、認証の実装に対してどんな印象を持っていますか?

いままで何人かの人にこの質問をぶつけてみましたが、 とても直感的にはわかりにくい という声を聞いております(かくいう僕自身、何度か挫折しました。。)。

よくわからないクラスがたくさん登場したり、必要な実装自体が非常に部分的だったりするので、全体像がよく見えなくなりがちです。

少しずつ全体像が見えるように。。

今回は Spring Security 認証の中でも、

  • 共通的にログイン認証を構成するクラス群とその役割

を説明していきます。

*今回は前回からの続編なので、先にこちらを読むことをお勧めします。

casual-tech-note.hatenablog.com

こちらの記事ではログイン認証の中でも

  • 結局ログインって何をやってるのか
  • 結局ログイン済みユーザーかどう判断しているのか

を Authentication オブジェクトを通してざっくりとみていきました。

今回はその中でも、

  • ユーザー名とパスワードをチェックするところ
  • Authentication オブジェクトを作成するところ

によりフォーカスしています。
f:id:shin-kinoshita:20181124163741p:plain

Filter による認証と AuthenticationManager

今回みていきます、ユーザー名とパスワードのチェックや Authentication オブジェクトの作成は Filter 処理で行われています。

実行されている Filter は UsernamePasswordAuthenticationFilter です(パスワード認証の場合)。
この Filter 処理では次のような処理が実行されます。

  1. Authentication オブジェクトを生成
    この時作成される Authentication オブジェクトはユーザー名とパスワードを保持します。
  2. AuthenticationManager の authenticate メソッドを実行
    Authentication オブジェクトが保持しているユーザー名やパスワードに問題がないかチェックします。

AuthenticationManager は次のように実装されています。

public interface AuthenticationManager {
    Authentication authenticate(Authentication authentication)
            throws AuthenticationException;
}

authenticate メソッドは引数として Authentication オブジェクトを受け取り、オブジェクトの情報が適切でなければ例外をスローする、という設計になっています。
f:id:shin-kinoshita:20181124163822p:plain

AuthenticationProvider と様々なパスワード認証

次に AuthenticationManager の authenticate メソッドが行なっている処理をより深ぼっていきます。

具体的に以下が実行されています。

  1. AuthenticationProvider の supports メソッドを実行
  2. AuthenticationProvider の authenticate メソッドを実行

ここで新しく AuthenticationProvider という別のインターフェースが登場します。
Authentication オブジェクトのチェックは AuthenticationManager によって行われるとのことでしたが、内部的には AuthenticationProvider が行います。

public interface AuthenticationProvider {
    Authentication authenticate(Authentication authentication)
            throws AuthenticationException;

    boolean supports(Class<?> authentication);
}

それぞれのメソッドは、

  • supports メソッド
    引数で与えられる型情報をサポートしていれば true を返す(Authentication オブジェクトの型が引数として与えられる)。
  • authenticate メソッド
    Authentication オブジェクトが保持する情報が適切かチェックする。

このような設計になっていることを理解するに当たって、パスワード認証そのものについて押さえておくべきことがあります。
それは、ここまで一口にパスワード認証ということで扱ってきましたが 実際にはパスワード認証にもいくつか種類が考えられる ということです。
例えば

  • 通常のパスワード認証
    セッションが切れると、再度ユーザー名とパスワードを入力してログインする認証方法。
  • Remember Me 認証
    初回だけしかユーザー名とパスワードを入力しないパスワード認証方法。
  • 代理ログイン認証
    別のアカウントとして代理でログインする認証方法。

などなど、他にもたくさん存在します。

そして認証方法によってログイン済みのユーザーかを判断するために必要な情報も異なります。
これに合わせて 今まで扱ってきた Authentication も認証方式ごとに実装クラスが存在しています。

また拡張性ということを考えてみましょう。
もしかしたら、この認証方式は今後も増える可能性もあります。
そうなると認証方式が増えるたびに AuthenticationManager の authenticate メソッドも変更する必要が出てきてしまい扱いが悪いです。

そのため 各認証方式ごとに AuthenticationProvider の実装クラスが用意され、認証処理が実装されています。

つまり 認証方式ごとに Authentication と AuthenticationProvider の実装クラスが対になって用意されているということです。

f:id:shin-kinoshita:20181124165932p:plain
認証方式ごとに存在する AuthenticationProvider と Authentication

さて、ここまでを踏まえて結局 AuthenticationManager の authenticate メソッドで行なっていることは、

  • 認証方式に合わせて AuthenticationProvider を選択
  • 選択された AuthenticationProvider で Authentication オブジェクトをチェック

以下に認証方式が Remember Me 認証の時の例を示しておきます。

f:id:shin-kinoshita:20181124182454p:plain

まとめ

最後に今まで扱ってきたクラス群の全体像を示しておきます。
f:id:shin-kinoshita:20181124184559p:plain

ざっというと、Filter 処理で Authentication オブジェクトを作成し AuthenticationManager -> AuthenticationProvider と渡って認証を行います。
次回は、各認証方式ごとに解説していきます。