01.Spring Securityの内部動作
概要
ここでは、Spring Securityの内部の仕組みと動作を見ていきます。
バージョンはSpring Security2.0について説明しますが、3.0についても動作は同じはずです。
また余裕ができましたら3.0についても調べて記事を書いてみたいと思います。
追記: 2013.07.07
3.x系ではセキュリティ用のフィルタなど、少しフィルタが増えているようです。
しかし、フィルタを使用していることは変わっていないので、以下の記事は3.x系でも役に立つかと思います。
一番下の3への変更やサンプルの記事も参照ください。
SpringSecurityの内部の仕組み
SpringSecurityはどのようにしてログイン認証、アクセス制御(認可処理)、ログアウト処理を実施しているのでしょうか?
それを見ていきたいと思います。
もし「まずは動作確認したい」ということでしたら、実際の設定サンプルをこの後に用意していますので、先にそちらをご覧ください。
内部処理の実際
SpringSecurityはtomcatのフィルタとして実装されています。
といっても1つのフィルタで実装されているのではなく、認証処理をいくつかの機能に分解してチェーンしています。
機能1つにつき1つのフィルタを用意し、順番に処理を実施していきます。
フィルタ(機能)は主に以下のものがあります。
中にはあるURLのパスにマッチする場合のみに動作し、それ以外はスルーして次のフィルタに行ってしまうものもあります。
・HttpSessionContextIntegrationFilter
Sessionにセキュリティ情報(SecurityContext)を設定するフィルタ。
SecurityContextの中には、認証手形(Authentication)などの情報を保持しています。
・LogoutFilter
ログアウト処理をするフィルタ
・AuthenticationProcessingFilter
認証処理をするフィルタ
・FilterSecurityInterceptor
アクセス制御をするフィルタ
フィルタの順番や機能はカスタマイズできますが、以下では通常の設定時の動作をみていきます。
Spring Securityのフィルタ処理の動作
あるURLのリクエストがきたときに各フィルタがどのように動作するかを大まかに記述しました。
図が横に入りきらなかったので2つに分けました。
フィルタ処理(その1)
【フィルタ処理 (ログインURLにアクセスした場合と、ログインせずに画面にアクセスした場合)】
<<フィルターの処理フロー>> <<ログインURLにアクセスした場合>> <<ログインせずにアクセスした場合>>
※画像の右側が切れている可能性がありますので、一度画像をクリックして全体を確認してみてください。
【図の見方】
フィルタはURLによって動作が変わります。
上記は、フィルタの処理順番と、URL毎にどのような処理をするかを描いています。
・一番左側 ⇒フィルタの処理順番を表しています。
・真ん中 ⇒ログインURL(/j_spring_security_check)にアクセスした場合の処理
・一番右側 ⇒ログインせずに画面(/sec/search.do)にアクセスした場合の処理
【認証手形について】
SpringSecurityには、Authenticationオブジェクトという認証手形のようなものがあります。
認証手形を持っていないユーザは認証されていないものとして、画面にアクセスすることができません。
認証手形は、ログイン認証が行われOKになると発行され、SecurityContextHolderというクラスにThreadLocalで保存されます。
以下では図と照らし合わせて、主な処理の説明をしていきます。
ログインURL(/j_spring_security_check)にアクセスした場合の処理
図の真ん中の説明になります。
①[1]LogoutFilterはURLがマッチしないので処理をスルーして次のフィルタ処理に移ります。
②[2]AuthenticationProcessingFilterは、URLが「/j_spring_security_check」のときにだけ動作します。
この場合は、URLがマッチするので認証処理を実施します。
実際に認証処理を管理するのは、AuthenticationManagerが行います。
AuthenticationManagerはいくつか認証手形発行所(AuthenticationProvider)を持つことができ、持っている認証手形発行所を順番に呼び出していきます。
受信したID/PWを元に認証手形発行所が認証処理を行い、認証OKであれば認証手形を発行します。
認証手形を取得できればそこで認証処理は終了します。
すべての発行所で手形を発行してもらえなかった場合、認証NGとなります。
<認証OKの場合>
まず、SecurityContextHolderに認証手形を保存します。SecurityContextHolderは、ThreadLocalに手形を保存します。
<認証NGの場合>
特になにもしません。
③返りの[0]HttpSessionContextIntegrationFilterで、認証手形をSecurityContextHolderから取り出し、Sessionに保存します。
もしSecurityContextHolderに認証手形がなければ保存はしません。
④ブラウザにレスポンスを返しますが、認証OK/NGで動作が違います。
<認証OKの場合>
リダイレクト機能により、設定していた認証OKのURL(この場合はトップ画面)に遷移します。
<認証NGの場合>
リダイレクト機能により、設定ファイルに設定していた認証エラー画面のURLに遷移します。
(設定ファイル上は、authentication-failure-url属性に設定した値です。)
以上がログインURLにアクセスした場合のフィルタの動作です。
ログインせずに画面(/sec/search.do)にアクセスした場合の処理
図の一番右側の説明になります。
ログインしていないので認証手形を持っていないことが前提となっています。
①[1]LogoutFilterはURLがマッチしないので処理をスルーして次のフィルタ処理に移ります。
②[2]AuthenticationProcessingFilterはURLがマッチしないので処理をスルーして次のフィルタ処理に移ります。
③[3]AnonymousProcessingFilterは、認証手形を持っていないユーザに対してAnonyumous認証手形を発行します。
この場合、認証手形を持っていませんでしたのでAnonyumous認証手形が発行され、SecurityContextHolderに保存されます。
④[6]FilterSecurityInterceptorは、/sec/search.do に対して、認証手形Anonymousに対して閲覧が許されているかをチェックします。
実際にチェックを管理するのは、AccessDecisionManagerになります。
AccessDecisionManagerは何人かの投票者(RoleVoter)を持っていて、1人ずつに投票をさせます。
すべての投票者がOKを出した場合に閲覧OKにすることもできますし、1人でも投票者がOKを出せば閲覧OKにすることもできます。
設定しだいです。
こうしてAccessDecisionManagerは閲覧許可を出すかどうか決めます。
結果が決まった後は以下のように動きます。
<アクセス許可された場合>
そのまま次のTomcatフィルタに流し、URLに応じた画面(/sec/search.do)の処理を実施します。
<アクセス拒否された場合>
AccessDenyException例外を発生します。
そうすると返りのフィルタで、[5]RequestExceptionTranslationFilterが例外をキャッチします。
ここで例外がAuthenticationExceptionの派生クラスかどうかをチェックし、派生クラスの場合は例外処理を行います。
今回の場合、Anonymous認証手形ですので、(閲覧権限エラーではなく)認証エラーと解釈して
ログイン画面にリダイレクトさせます。
以上がログインせずにアクセスした場合のフィルタの動作です。
フィルタ処理(その2)
【フィルタ処理 (ログイン後にアクセスした場合と、ログアウト処理の場合)】
<<フィルターの処理フロー>> <<ログイン後にアクセスした場合>> <<ログアウト処理の場合>>
※画像の右側が切れている可能性がありますので、一度画像をクリックして全体を確認してみてください。
ログイン後に画面(/sec/search.do)にアクセスした場合の処理
図の真ん中の説明になります。
ログイン後ですので、認証手形を持っています。
①[1]LogoutFilterはURLがマッチしないので処理をスルーして次のフィルタ処理に移ります。
②[2]AuthenticationProcessingFilterはURLがマッチしないので処理をスルーして次のフィルタ処理に移ります。
③[4]AnonymousProcessingFilterは、認証手形を持っているユーザには何もしません。次のフィルタ処理に移ります。
④[6]FilterSecurityInterceptorは、/sec/search.do に対して、認証手形①の閲覧が許されているかをチェックします。
チェックの動作は「ログインせずに画面(/sec/search.do)にアクセスした場合」の④と同じになります。
<アクセス許可された場合>
そのまま次のTomcatフィルタに流し、URLに応じた画面(/sec/search.do)の処理を実施します。
<アクセス拒否された場合 >
[6]FilterSecurityInterceptorは、AccessDeniedExceptionを投げます。
返りの[5]RequestExceptionTranslationFilterで例外をキャッチします。
認証された手形(Anonymousでない手形)を持っているユーザがAccessDeniedExceptionを投げたので、
[5]RequestExceptionTranslationFilterは、権限エラーと解釈します。
リダイレクトでaccess-deny用の画面(権限エラー画面)に遷移します。
以上が、ログイン後に画面にアクセスした場合のフィルタの動作です。
ログアウトURL(/logout)にアクセスした場合の処理
図の真ん中の説明になります。
ログイン後ですので、認証手形を持っています。
①[1]LogoutFilterはURLがマッチするのでログアウト処理を実施します。
具体的にはSessionから認証手形を削除します。
その後、リダイレクト機能によりログイン画面に遷移します。
以上が、ログインアウト画面にアクセスした場合のフィルタの動作です。
まとめ
Spring Securityの内部動作はフィルタで記述されています。
フィルタの数が多いので少し及び腰になりますが、ひとつずつの動作を見ていくとそれ程難しいことをしているわけではないことに気づきます。
上記の内部動作を知っていれば機能拡張もやりやすいのではないかと思います。
Created Date: 2010/01/02