.. raw:: pdf PageBreak 認証 ================================================================================ .. contents:: 目次 :local: Overview -------------------------------------------------------------------------------- 本節では、Spring Securityで提供している認証機能を説明する。 Spring Securityでは、設定ファイルの記述のみで、ユーザ認証を実装することができる。 Spring Securityで提供している認証方式として、DB認証、LDAP認証、CAS認証、JAAS認証、X509認証、Basic認証がサポートされているが、本ガイドラインでは、DB認証についてのみ説明する。 .. tip:: DB認証以外の詳細は、各認証方式の公式ドキュメントを参照されたい。 * \ `LDAP Authentication `_\ * \ `CAS Authentication `_\ * \ `Java Authentication and Authorization Service (JAAS) Provider `_\ * \ `X.509 Authentication `_\ * \ `Basic and Digest Authentication `_\ Login ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Spring Securityによるログイン処理の流れを以下に示す。 .. figure:: ./images/Authentication_Login_overview.png :alt: Authentication(Login) :width: 80% :align: center #. 認証処理を指定したリクエストを受信すると、認証フィルタが起動する。 #. 認証フィルタは、リクエストからユーザ、パスワードを抽出し、認証情報を生成する。 生成した認証情報をパラメータとし、認証マネージャの認証処理を実行する。 #. 認証マネージャは、指定された認証プロバイダの認証処理を実行する。 認証プロバイダは、データソース(DBやLDAP)からユーザ情報を取得し、パスワード照合等のユーザ認証を行う。 認証成功時には、認証済みの情報を保持する認証情報を作成し、 認証マネージャに返す。認証失敗の場合は、認証失敗例外を送出する。 #. 認証マネージャは、受け取った認証情報を認証フィルタに返す。 #. 認証フィルタは、受け取った認証情報(認証済み)をセッションに格納する。 #. 認証成功時は、認証前のセッション情報を初期化し、新たにセッション情報を作成する。 #. 指定された認証成功/失敗時のパスへリダイレクトする。セッションIDをクライアントに返却する。 Logout ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Spring Securityによるログアウト処理の流れを以下に示す。 .. figure:: ./images/Authentication_Logout_overview.png :alt: Authentication(Logout) :width: 80% :align: center #. 指定されたログアウト処理へのリクエストを受信すると、ログアウトフィルタが起動する。 #. ログアウトフィルタはセッション情報を破棄する。 また、クライアントのクッキー(図中のCookie)を破棄するようなレスポンスを設定する。 #. 指定されたログアウト時のパスへ、リダイレクトする。 \ .. note:: ログアウト後、残存するセッション情報が第三者に利用されることによるなりすましを防ぐため、 セッション情報は、ログアウト時に\ ``org.springframework.security.web.session.ConcurrentSessionFilter``\ で破棄される。 | How to use -------------------------------------------------------------------------------- | 認証機能を使用するために、Spring Securityの設定ファイルに記述する内容を以下に示す。 | 基本設定については、\ :doc:`SpringSecurity`\ を参照されたい。 \ ````\ 要素の設定 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | 以下の設定例のように、spring-security.xmlの\ ````\ 要素の\ ``auto-config``\ 属性を\ ``true``\ とすることで、 | Spring Securityの認証機能の基本的な設定を、省略することができる。 .. code-block:: xml .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ``auto-config="true"``\ と設定することで、 | \ ````\ 、\ ````\ 、\ ````\ 要素を設定しなくても有効になる。 .. note:: \ ````\ 、\ ````\ 、\ ````\ 要素について説明する。 .. list-table:: :header-rows: 1 :widths: 15 85 * - 要素名 - 説明 * - | \ ````\ - | \ ``org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter``\ が有効になる。 | UsernamePasswordAuthenticationFilterは、ユーザ名、パスワードをPOST時に、リクエストから取り出し、認証を行うFilterである。 | 詳細は、\ :ref:`form-login`\ を参照されたい。 * - | \ ````\ - | \ ``org.springframework.security.web.authentication.www.BasicAuthenticationFilter``\ が有効になる。 | BasicAuthenticationFilterは、Basic認証の処理を実施するFilterであり、RFC1945に準拠して実装されている。 | 詳細な利用方法は、\ `BasicAuthenticationFilter JavaDoc `_\ を参照されたい。 * - | \ ````\ - | \ ``org.springframework.security.web.authentication.logout.LogoutFilter``\ , | \ ``org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler``\ が有効になる。 | LogoutFilterは、ログアウト時に呼ばれるFilterであり、 | \ ``org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices``\ (Cookieの削除) や、 | SecurityContextLogoutHandler(セッションの無効化)を呼び出している。 | 詳細は、\ :ref:`form-logout`\ を参照されたい。 .. _form-login: \ ````\ 要素の設定 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | 本節では、\ ````\ 要素の設定方法を説明する。 | | form-login要素の属性について、以下に示す。 spring-security.xml .. code-block:: xml .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ``login-page``\ 属性にログインフォーム画面のパスを指定する。 | 「未認証ユーザ」が「認証ユーザ」しかアクセスできないページにアクセスした際に、 | 強制リダイレクトさせるパス。 * - | (2) - | \ ``default-target-url``\ 属性に認証成功時の遷移先パスを指定する。指定がない場合、"/"が、デフォルトのパスになる。 * - | (3) - | \ ``login-processing-url``\ 属性に認証処理を行うパスを指定する。指定がない場合、「j_spring_security_check」がデフォルトのパスになる。 | **本ガイドラインでは、上記のデフォルト値「j_spring_security_check」を使用せず、システム独自の値に変更することを推奨する。**\ この例では"/authentication"を指定している。 * - | (4) - | ログイン成功後に\ ``default-target-url``\ に指定したパスに常に遷移するかどうかを\ ``always-use-default-target``\ 属性に設定する。 | デフォルトは、\ ``false``\ である。\ ``false``\ に設定されている場合、認証成功のハンドラの基底クラスである\ ``org.springframework.security.web.authentication.AbstractAuthenticationTargetUrlRequestHandler``\ で | リダイレクト先が指定されていれば、指定先に遷移する。指定がない場合、\ ``default-target-url``\ に指定したパスに遷移する。 * - | (5) - | \ ``authentication-failure-url``\ に認証失敗時の遷移先を設定する。 | \ ``authentication-failure-handler-ref``\ 属性の指定がない場合、認証エラーの種別を問わず、一律、本設定の遷移先に遷移する。 * - | (6) - | \ ``default-target-url``\ 属性に認証失敗時に呼ばれる、ハンドラクラスを指定する。 | 詳細は、\ :ref:`authentication-failure-handler-ref`\ を参照されたい。 * - | (7) - | \ ``default-target-url``\ 属性に認証成功時に呼ばれる、ハンドラクラスを指定する。 上記以外の属性については、\ `Spring Securityのマニュアル `_\ を参照されたい。 .. warning:: **Spring Security のデフォルト値「j_spring_security_check」の使用を推奨しない理由** デフォルト値を使用している場合、そのアプリケーションが、Spring Securityを使用していることについて、露見してしまう。 そのため、Spring Securityの脆弱性が発見された場合、脆弱性をついた攻撃を受けるリスクが高くなる。 前述のリスクを避けるためにも、デフォルト値を使用しないことを推奨する。 .. _form-login-JSP: ログインフォームの作成 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | 認証時に使用するログインフォームをJSPで作成する。 * src/main/webapp/WEB-INF/views/login.jsp .. code-block:: jsp .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | formのaction属性に認証処理を行うための遷移先を指定する。 | 遷移先のパスはlogin-processing-url属性で指定した、/authentication を指定すること。 | ${pageContext.request.contextPath}/authenticationにアクセスすることで認証処理が実行される。 | HTTPメソッドは、「POST」を指定すること。 * - | (4) - | 認証処理において、「ユーザID」として扱われる要素。 | name属性には、Spring Securityのデフォルト値である「j_username」を指定すること。 * - | (5) - | 認証処理において、「パスワード」として扱われる要素。 | name属性には、Spring Securityのデフォルト値である「j_password」を指定すること。 認証エラーメッセージを表示する場合は以下の追加する .. code-block:: jsp .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | リクエストパラメータに設定されたエラーメッセージの判定を行う。 | form-login要素のauthentication-failure-url属性に設定された値や、 | 認証エラーハンドラの"defaultFailureUrl"に設定された値によって、判定処理を変更する必要があるので注意すること。 | 本例では、authentication-failure-url="/login?error=true"のような設定がある場合の、例を示している。 * - | (2) - | 認証エラー時に出力させる例外メッセージを出力する。 | 共通ライブラリで提供している\ ``org.terasoluna.gfw.web.message.MessagesPanelTag``\ を指定して出力させることを推奨する。 | 「\ ````\ 」タグの使用方法は、\ :doc:`../ArchitectureInDetail/MessageManagement`\ を参照されたい。 * spring-mvc.xml ログインフォームを表示するControllerを定義する。 .. code-block:: xml .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | "/login"にアクセスされたら、view名として"login"を返却するだけのControllerを定義する。\ ``InternalResourceViewResolver``\ によってsrc/main/webapp/WEB-INF/views/login.jspが出力される。 | この単純なコントローラはJavaによる実装が不要である。 .. note:: 上記の設定は次のControllerと同義である。 .. code-block:: java @Controller @RequestMapping("/login") public class LoginController { @RequestMapping public String index() { return "login"; } } 単純にview名を返すだけのメソッドが一つだけあるControllerが必要であれば、\ ````\ を使用すればよい。 ログインフォームをController経由で表示するメリットは、CSRFトークンを自動で埋め込める点にある。Controllerを経由しない場合は、 \ :doc:`Tutorial`\ で実施したようにjspに直接CSRFトークンを埋め込む必要がある。 チュートリアルではController作成の説明を省くために、ログインフォームの表示はControllerを経由していない。CSRF対策の詳細は\ :doc:`CSRF`\ を参照されたい。 ログインフォームの属性名変更 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 「j_username」、「j_password」は、Spring Securityのデフォルト値である。\ ````\ 要素の設定で、任意の値に変更することができる。 * spring-security.xml \ ``username``\ 、\ ``password``\ の属性 .. code-block:: xml .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ``username-parameter``\ 属性で\ ``username``\ の入力フィールドの\ ``name``\ 属性を、「username」に変更している。 * - | (2) - | \ ``password-parameter``\ 属性で\ ``password``\ の入力フィールドの\ ``name``\ 属性を、「password」に変更している。 認証処理の設定 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Spring Securityにおける認証処理の設定は\ ``AuthenticationProvider``\ と\ ``UserDetailsService``\ の設定が \ ``AuthenticationProvider``\ は、次の役割を担う。 * 認証に成功した場合、認証ユーザー情報を返却する * 認証に失敗した場合、例外をスローする \ ``UserDetailsService``\ は、認証ユーザー情報を永続化層から取得する役割を担う。 それぞれデフォルトで用意されているものを使用してもよいし、独自拡張して使用しても良い。 組み合わせも自由である。 \ ``AuthenticationProvider``\ クラスの設定 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | \ ``AuthenticationProvider``\ の実装として、DB認証を行うためのプロバイダ\ ``org.springframework.security.authentication.dao.DaoAuthenticationProvider``\ を使用する方法を説明する。 * spring-security.xml .. code-block:: xml .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ````\ 要素内に\ ````\ 要素を定義する。複数指定して、認証方法を組み合わせることが可能であるが、ここでは説明しない。 * - | (2) - | \ ````\ 要素で\ ``AuthenticationProvider``\ を定義する。デフォルトで、\ ``DaoAuthenticationProvider``\ が有効になる。これ以外の\ ``AuthenticationProvider``\ を指定する場合は\ `ref属性で、対象のAuthenticationProviderのBean IDを指定する `_\ 。 | | \ ``user-service-ref``\ 属性に、認証ユーザ情報を取得する\ ``UserDetailsService``\ のBean Idを指定する。\ ``DaoAuthenticationProvider``\ を使用する場合、この設定は必須である。 | 詳細は、\ :ref:`userDetailsService`\ を参照されたい。 * - | (3) - | パスワード照合時に、フォームから入力されたパスワードのエンコードを行うクラスのBean IDを指定する。 | 指定がない場合に、「平文」でパスワードが扱われる。詳細は、\ :doc:`PasswordHashing`\ を参照されたい。 | 「ユーザーID」と「パスワード」だけで永続化層からデータを取得し、認証するという要件であればこの\ ``DaoAuthenticationProvider``\ を使用すれば良い。 | 永続化層からのデータ取得方法は次に説明する\ ``UserDetailsService``\ で決める。 .. _userDetailsService: \ ``UserDetailsService``\ クラスの設定 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | \ ``AuthenticationProvider``\ の\ ``userDetailsService``\ プロパティに指定したBeanを設定する。 \ ``UserDetailsService``\ は次のメソッドをもつインタフェースである。 .. code-block:: java UserDetails loadUserByUsername(String username) throws UsernameNotFoundException このインタフェースを実装すれば、任意の保存場所から認証ユーザー情報を取得することができる。 ここでは、JDBCを使用して、DBからユーザ情報を取得する \ ``org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl``\ を説明する。 \ ``JdbcDaoImpl``\ を使用するにはspring-security.xmlに以下のBean定義を行えば良い。 .. code-block:: xml | \ ``JdbcDaoImpl``\ は、認証ユーザー情報と認可情報を取得するためのデフォルトSQLを定義しており、これらに対応したテーブルが用意されていることが前提となっている。前提としているテーブル定義は\ `Spring Securityのマニュアル `_\ を参照されたい。 | 既存のテーブルからユーザー情報、認可情報を取得したい場合は、発行されるSQLを既存のテーブルに合わせて修正すればよい。 | 使用するSQLは以下の3つである。 * \ `ユーザ情報取得クエリ `_\ | ユーザ情報取得クエリに合致するテーブルを作成することで、後述する設定ファイルへのクエリ指定が不要となる。 | 「username」、「password」、「enabled」フィールドは必須であるが、 | 後述する設定ファイルへのクエリ指定で、別名を付与することにより、テーブル名、カラム名が一致しなくても問題ない。 | 例えば次のようなSQLを設定すれば「email」カラムを「username」として使用することができ、「enabled」は常に\ ``true``\ となる。 .. code-block:: sql SELECT email AS username, pwd AS password, true AS enabled FROM customer WHERE email = ? | \ :ref:`form-login-JSP`\ で前述した、「ユーザID」がクエリのパラメータに指定される。 * \ `ユーザ権限取得クエリ `_\ | ユーザに対する認可情報を取得するクエリである。 * \ `グループ権限取得クエリ `_\ | ユーザーが所属するグループの認可情報を取得するクエリである。グループ権限はデフォルトでは無効になっており、本ガイドラインでも扱わない。 | 以下に、DBの定義例、Spring Securityの設定ファイル例を示す。 | テーブルの定義について | DB認証処理を実装するにあたり、必要となるテーブルを定義する。 | 前述した、デフォルトのユーザ情報取得クエリ合致するテーブルとなっている。 | そのため、下記が最低限必要となるテーブルの定義となる(物理名は仮称)。 テーブル名: account .. list-table:: :header-rows: 1 :widths: 15 15 10 60 * - 論理名 - 物理名 - 型 - 説明 * - ユーザID - username - 文字列 - ユーザを一意に識別するためのユーザID。 * - パスワード - password - 文字列 - ユーザパスワード。ハッシュ化された状態で格納する。 * - 有効フラグ - enabled - 真偽値 - 無効ユーザ、有効ユーザを表すフラグ。「false」に設定されたユーザは無効ユーザとして、認証エラーとなる。 * - 権限名 - authority - 文字列 - 認可機能を必要としない場合は不要。 \ ``JdbcDaoImpl``\ をカスタマイズして設定する例を以下に示す。 .. code-block:: xml .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | 権限名のprefixを指定する。DB上に格納されている権限名が"USER"の場合、この認証ユーザーオブジェクトが持つ権限名は"ROLE_USER"になる。 | 認可機能と命名規則を合わせて設定する必要がある。認可機能の詳細は、\ :doc:`Authorization`\ を参照されたい。 * - | (2) - | 認可機能において、「グループ権限」の概念を用いる場合に指定する。 | 本ガイドラインでは扱わない。 * - | (3) - | ユーザ情報を取得するクエリを設定する。取得するデータは、「ユーザID」、「パスワード」、「有効フラグ」の順とする。 | 「有効フラグ」による認証判定を行わない場合には、「有効フラグ」のSELECT結果を「true」固定とする。 | なお、ユーザを一意に取得できるクエリを記述すること。複数件数取得された場合には、1件目のレコードがユーザとして使われる。 * - | (4) - | ユーザの権限を取得するクエリを設定する。取得するデータは、「ユーザID」、「権限ID」の順とする。 | 認可の機能を使用しない場合は、「権限ID」は任意の固定値でよい。 .. note:: クエリを変更するだけでは実現できない認証を行う場合、\ ``UserDetailsService``\ を拡張して実現する必要がある。 拡張方法については、\ :ref:`extendsuserdetailsservice`\ を参照されたい。 \ ``UserDetails``\ クラスの利用方法 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | 認証に成功した後に\ ``UserDetailsService``\ が作成した\ ``UserDetails``\ の利用方法について、説明する。 Javaクラスで\ ``UserDetails``\ オブジェクトを利用する """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | 認証に成功した後、\ ``UserDetails``\ クラスは | \ ``org.springframework.security.core.context.SecurityContextHolder``\ に格納される。 \ ``SecurityContextHolder``\ から\ ``UserDetails``\ を取得する例を示す。 .. code-block:: java public static String getUsername() { Authentication authentication = SecurityContextHolder.getContext() .getAuthentication(); // (1) if (authentication != null) { Object principal = authentication.getPrincipal(); // (2) if (principal instanceof UserDetails) { return ((UserDetails) principal).getUsername(); // (3) } return (String) principal.toString(); } return null; } .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ``SecurityContextHolder``\ から\ ``org.springframework.security.core.Authentication``\ オブジェクトを取得する。 * - | (2) - | \ ``Authentication``\ オブジェクトから\ ``UserDetails``\ オブジェクトを取得する。 * - | (3) - | \ ``UserDetails``\ オブジェクトから、ユーザ名を取得する。 \ ``SecurityContextHolder``\ から\ ``UserDetails``\ オブジェクトを取得する方法は、どこからでもstaticメソッドで利用可能であり、 便利な反面、モジュール結合度を高めてしまう。テストも実施しづらい。 \ ``UserDetails``\ オブジェクトは\ ``java.security.Principal``\ オブジェクトからも取得可能であるため、Spring MVCのController内では以下のように\ ``SecurityContextHolder``\ を使用せずに\ ``UserDetails``\ オブジェクトを取得できる。 .. code-block:: java @RequestMapping(method = RequestMethod.GET) public String view(Principal principal, Model model) { // get Authentication Authentication authentication = (Authentication) principal; // (1) // get UserDetails UserDetails userDetails = (UserDetails) authentication.getPrincipal(); // (2) // omitted ... } .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ``java.security.Principal``\ オブジェクトを\ ``org.springframework.security.core.Authentication``\ クラスにキャストする。 * - | (2) - | \ ``Authentication``\ オブジェクトから\ ``UserDetails``\ オブジェクトを取得する。 \ **Controller内でUserDetailsオブジェクトにアクセスする場合はこちらの方法を推奨する**\ 。 .. note:: ServiceクラスではControllerが取得した\ ``UserDetails``\ オブジェクトの情報を使用し、\ ``SecurityContextHolder``\ は使用しないことを推奨する。 \ ``SecurityContextHolder``\ は\ ``java.security.Principal``\ オブジェクトにアクセスできないメソッド内でのみ利用することが望ましい。 JSPで\ ``UserDetails``\ にアクセスする """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | Spring Securityでは、JSPで認証情報を利用するための仕組みとして、JSP taglibを提供している。このtaglibを使うために以下の宣言が必要である。 .. code-block:: jsp <%@ taglib uri="http://www.springframework.org/security/tags" prefix="sec"%> .. note:: \ `TERASOLUNA Global Frameworkの雛形 `_\ を使用している場合はWEB-INF/views/common/include.jspに設定済みである。 | 認証ユーザ名をJSPで表示する場合を例に、使用方法を示す。 .. code-block:: jsp .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ```` \ タグで\ ``Authentication``\ オブジェクトにアクセスでき、\ ``property``\ 属性に指定したプロパティアクセスできる。この例では\ ``getPrincipal().getUsername()``\ の結果を出力する。 .. code-block:: jsp ${f:h(userDetails.username)} .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ``property``\ 属性に指定したプロパティを\ ``var``\ 属性にした名前で変数に格納できる。 * - | (2) - | (1)で変数に格納した後はJSP内で\ ``UserDetails``\ にアクセスできる。 .. note:: Controller内で\ ``UserDetails``\ を取得して\ ``Model``\ に追加することもできるが、JSPに表示する際はJSPタグを使用すればよい。 .. note:: :ref:`userDetailsService`\ で説明した\ ``JdbcDaoImpl``\ が生成する\ ``UserDetails``\ は「ユーザーID」や「権限」といった最低限の情報しか保持していない。 画面の表示項目として「ユーザー姓名」など他のユーザー情報が必要な場合は\ ``UserDetails``\ と \ ``UserDetailsService``\ を拡張する必要がある。 拡張方法については、\ :ref:`extendsuserdetailsservice`\ を参照されたい。 .. _authentication(spring_security)_how_to_use_sessionmanagement: Spring Securityにおけるセッション管理 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ログイン時のセッション情報の生成方式や、例外発生時の設定を行う方法について説明する。 | \ ````\ タグを指定することで、\ ``org.springframework.security.web.session.SessionManagementFilter``\ が有効になる。 | 以下にspring-security.xmlの設定例を示す。 .. code-block:: xml .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ``create-session``\ 属性でセッションの作成方針を指定する。 | 以下の値を指定することができる。 | \ ``always``\ : Spring Securityは、既存のセッションがない場合にセッションを新規作成する、セッションが存在すれば、再利用する。 | \ ``ifRequired``\ : Spring Securityは、セッションが必要であれば作成する。デフォルトの設定である。セッションがすでにあれば、作成せずに再利用する。 | \ ``never``\ : Spring Securityは、セッションを作成しないが、セッションが存在すれば、再利用する。 | \ ``stateless``\ : Spring Securityは、セッションを作成しない、セッションが存在しても使用しない。そのため、毎回認証を行う必要がある。 * - | (2) - | \ ``invalid-session-url``\ 属性で無効なセッションIDがリクエストされた場合に遷移するパスを指定する。 | 設定しない場合、\ ``org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy``\ | の設定に依存したパスに遷移する。 * - | (3) - | \ ``org.springframework.security.web.authentication.session.SessionAuthenticationStrategy``\ で | 例外が発生した場合、遷移するパスに\ ``session-authentication-error-url``\ 属性に指定する。 | 指定しない場合、\ ``org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler``\ | の設定に依存する。 * - | (4) - | \ ``session-fixation-protection``\ 属性でセッション管理方式を指定する。 | 以下の値を指定することができる。 | \ ``migrateSession``\ :ログイン前のセッション情報を引き継ぎ(コピー)、IDのみ新規作成する。デフォルトの設定である。 | \ ``newSession``\ :ログイン前のセッション情報を引き継がず、ID、セッション内容を新規作成する。 | | 本機能の目的は、新しいセッションIDをログイン毎に割り振ることで、\ `セッション・フィクセーション攻撃 `_\を防ぐことにある。そのため、明確な意図がない限り、デフォルトの設定を推奨する。 * - | (5) - | \ ``session-authentication-strategy-ref``\ 属性でセッションチェックの振る舞いを決める\ ``org.springframework.security.core.Authentication.SessionAuthenticationStrategy``\ クラスのBean IDを指定する。 \ ``SessionAuthenticationStrategy``\ の設定 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" \ ``SessionAuthenticationStrategy``\ の設定例を以下に示す。 .. code-block:: xml .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ``org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy``\ | のコンストラクタの引数に、listで指定する。 * - | (2) - | 本例では、CSRFチェックを行う\ ``org.springframework.security.web.csrf.CsrfAuthenticationStrategy``\ | を指定している。CSRF対策については、\ :doc:`CSRF`\ を参照されたい。 同時セッション数の制御 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | Spring Securityでは、1ユーザが保持できる最大セッション数を、任意に変更できる機能(\ `Concurrent Session Control `_\ )を提供している。 | ここでいうユーザとは、\ ``Authentication.getPrincipal()``\ で取得される、認証ユーザーオブジェクトのことである。 | 最大セッション数を超えた場合の制御方法は、次のパターンが存在する。業務要件によって使い分けること。 #. 1ユーザの最大セッション数を超過した場合、最も使用されていないユーザを無効にする (後勝ち) #. 1ユーザの最大セッション数を超過した場合、新規ログインを受け付けない (先勝ち) どちらの場合も、この機能を有効にするためにはweb.xmlに以下の設定を追加する必要がある。 .. _HttpSessionEventPublisher-ref: .. code-block:: xml org.springframework.security.web.session.HttpSessionEventPublisher .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | Concurrent Session Control を使用するに当たり、\ ``org.springframework.security.web.session.HttpSessionEventPublisher``\ を、listenerに定義する必要がある。 1. 最も使用されていないユーザを無効にする場合 spring-security.xmlに以下の設定を追加する。 .. code-block:: xml .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ````\ 要素の、\ ``position``\ 属性に\ ``CONCURRENT_SESSION_FILTER``\ を指定する。 * - | (2) - | \ ``org.springframework.security.web.session.ConcurrentSessionFilter``\ クラスをBean定義する。 * - | (3) - | コンストラクタの第1引数に、\ ``org.springframework.security.core.session.SessionRegistryImpl``\ を参照指定する。 * - | (4) - | コンストラクタの第2引数に、期限切れになったセッションが遷移するパスを指定する。 * - | (5) - | CompositeSessionAuthenticationStrategyの第1引数に、 | \ ``org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy``\ を指定する。 * - | (6) - | コンストラクタの第1引数に、\ ``org.springframework.security.core.session.SessionRegistryImpl``\ を参照指定する。 * - | (7) - | \ ``maximumSessions``\ 属性に、1ユーザが許容する最大セッション数を定義することができる。 | 上記例では、1を指定しているため、1ユーザが許容するセッションは1つになる。 | ユーザーが複数のブラウザでログインした場合、使用した日時が最も古いセッションに対して期限切れにする。 | 指定しない場合、1が設定される。 * - | (8) - | \ ``org.springframework.security.core.session.SessionRegistry``\ インタフェースを実装したクラスである、 | \ ``org.springframework.security.core.session.SessionRegistryImpl``\ を指定する。 2. 新規ログインを受け付けない spring-security.xmlに以下の設定を行う。 .. code-block:: xml :emphasize-lines: 16 .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ``exceptionIfMaximumExceeded``\ 属性を\ ``true``\ に設定することにより、 最大セッション数を超過した場合、 | \ ``org.springframework.security.web.authentication.session.SessionAuthenticationException``\ がスローされる。 | そのため、\ ``ConcurrentSessionFilter``\ の第2引数で定義したパスには遷移しないので注意すること。 | \ ``exceptionIfMaximumExceeded``\ 属性の設定を省略した場合は、\ ``false``\ が設定される。 .. tip:: ````\ 要素の\ ``session-authentication-strategy-ref``\ 属性を指定せず、\ ````\ 要素の子要素として\ ` `_\ 要素を使用することもできる。 .. _authentication-failure-handler-ref: 認証エラー時のハンドラクラスの設定 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | \ ````\ 要素の\ ``authentication-failure-handler-ref``\ 属性に | \ ``org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler``\ クラスの設定をし、 | 認証エラー時に送出される例外と、それに対応した遷移先を指定できる。 | 指定する遷移先は、未認証ユーザがアクセス可能であること。 spring-security.xml .. code-block:: xml /login/badCredentials /login/usernameNotFound /login/disabled /login/providerNotFound /login/authenticationService .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | エラー時のデフォルトの遷移先パスを指定する。 | 後述する\ ``exceptionMappings``\ プロパティに定義していない例外が発生した場合、本プロパティで指定した遷移先に遷移する。 * - | (2) - | catchする例外と、例外発生時の遷移先を、リスト形式で指定する。 | keyに例外クラスを指定し、値に遷移先を設定する。 .. _SpringSecurity-Exception: Spring Securityがスローする代表的な例外を、以下に記述する。 .. list-table:: :header-rows: 1 :widths: 10 25 65 * - 項番 - エラーの種類 - 説明 * - | (3) - \ ``BadCredentialsException``\ - パスワード照合失敗による認証エラー時にスローされる。 * - | (4) - \ ``UsernameNotFoundException``\ - | 不正ユーザID(存在しないユーザID)による認証エラー時にスローされる。 | \ ``org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider``\ を継承したクラスを認証プロバイダに指定している場合、 | \ ``hideUserNotFoundExceptions``\ を\ ``false``\ に変更しないと上記例外は、\ ``BadCredentialsException``\ に変更される。 * - | (5) - \ ``DisabledException``\ - 無効ユーザIDによる認証エラー時に、スローされる。 * - | (6) - \ ``ProviderNotFoundException``\ - | 認証プロバイダクラス未検出エラー時にスローされる。 | 設定誤り等の理由から、認証プロバイダクラスが不正な場合に発生する。 * - | (7) - \ ``AuthenticationServiceException``\ - | 認証サービスエラー時にスローされる。 | DB接続エラー等、認証サービス内で何らかのエラーが発生した際に発生する。 .. warning:: 本例では、\ ``UsernameNotFoundException``\ をハンドリングして遷移させているが、 ユーザIDが存在しないことを利用者に知らせると、特定のIDの存在有無が判明するため、セキュリティの観点上望ましくない。 そのため、ユーザに通知するメッセージには、例外の種類によって区別をしない画面遷移、メッセージにした方がよい。 .. _form-logout: \ ````\ 要素の設定 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | 本節では、\ ````\ 要素の設定方法を説明する。 spring-security.xml .. code-block:: xml .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ``logout-url``\ 属性に、ログアウト処理を実行するためのパスを指定する。 * - | (2) - | \ ``logout-success-url``\ 属性に、ログアウト後の遷移先パスを指定する。 * - | (3) - | \ ``invalidate-session``\ 属性に、ログアウト時にセッションを破棄するかを設定する。デフォルトは\ ``true``\ である。\ ``true``\ の場合、ログアウト時にセッションがクリアされる。 * - | (4) - | \ ``delete-cookies``\ 属性に、指定したクッキーを削除する。複数記述する場合は「,」で区切る。 * - | (5) - | \ ``success-handler-ref``\ 属性に、ログアウト成功後に呼び出される、ハンドラクラスを指定する。 \ ````\ 要素の設定 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | 「\ `Remeber Me `_\ 」とは、websiteに頻繁にアクセスするユーザの利便性を、高めるための機能の一つとして、 | ログイン状態を保持する機能である。 | 本機能は、ユーザがログイン状態を保持することを許可していた場合、ブラウザを閉じた後も | cookieにログイン情報を保持し、ユーザ名、パスワードを再入力しなくともログインすることができる機能である。 | \ ````\ 要素の属性について、以下に示す。 spring-security.xml .. code-block:: xml .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ``key``\ 属性に、Remeber Me用のcookieを保持しておくためのユニークなキーを指定する。 | 指定が無い場合、ユニークなキーを起動時に生成するため、起動時間向上を考えた場合指定しておくことを推奨する。 * - | (2) - | 「\ ``token-validity-seconds``\ 属性に、Remeber Me用のcookieの有効時間を秒単位で指定する。この例では30日間を設定している。 | 指定が無い場合、デフォルトで14日間が有効期限になる。 上記以外の属性については、\ `Spring Securityのマニュアル `_\ を参照されたい。 ログインフォームには以下のように「Remeber Me」機能を有効にするためのフラグを用意する必要がある。 .. code-block:: jsp :emphasize-lines: 7-9
.. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | HTTPパラメータに、\ ``_spring_security_remember_me``\ を設定することで、 | \ ``true``\ でリクエストされた場合、次回の認証を回避することができる。 How to extend -------------------------------------------------------------------------------- .. _extendsuserdetailsservice: \ ``UserDetailsService``\ の拡張 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | 認証時にユーザID、パスワード以外の情報も取得したい場合、 * \ ``org.springframework.security.core.userdetails.UserDetails``\ * \ ``org.springframework.security.core.userdetails.userDetailsService``\ を実装する必要がある。 ログインユーザーの氏名や所属部署などの付属情報を常に画面のヘッダーに表示させる必要がある場合、毎リクエストでDBから取得するのは非効率的である。 \ ``UserDetails``\ オブジェクトに保持させて、\ ``SecurityContext``\ や\ ````\ タグからアクセスできようにするにはこの拡張が必要である。 \ ``UserDetails``\ の拡張 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 認証情報以外に顧客情報も保持する\ ``ReservationUserDetails``\ クラスを作成する。 .. code-block:: java public class ReservationUserDetails extends User { // (1) // omitted private final Customer customer; // (2) private static final List DEFAULT_AUTHORITIES = Collections .singletonList(new SimpleGrantedAuthority("ROLE_USER")); // (3) public ReservationUserDetails(Customer customer) { super(customer.getCustomerCode(), customer.getCustomerPassword(), true, true, true, true, DEFAULT_AUTHORITIES); // (4) this.customer = customer; } public Customer getCustomer() { // (5) return customer; } } .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ``UserDetails``\ のデフォルトクラスである、\ ``org.springframework.security.core.userdetails.User``\ クラスを継承する。 * - | (2) - | 認証情報および顧客情報をもつDomainObjectクラスを保持する。 * - | (3) - | 認可情報を、\ ``org.springframework.security.core.authority.SimpleGrantedAuthority``\ のコンストラクタで作成する。ここでは"ROLE_USER"という権限を与える。 | | 本実装は簡易実装であり、本来は認可情報はDB上の別のテーブルから取得すべきである。 * - | (4) - | スーパークラスのコンストラクタに、DomainObjectが持つユーザID、パスワードを設定する。 * - | (5) - | \ ``UserDetails``\ 経由で顧客情報にアクセスするためのメソッド。 .. note:: \ ``User``\ クラスを継承するだけでは、業務用件を実現できない場合、\ ``UserDetails``\ インタフェースを実装すればよい。 独自\ ``UserDetailsService``\ の実装 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | \ ``UserDetailsService``\ を実装したReservationUserDetailsServiceクラスを作成する。 | 本例では、\ ``Customer``\ オブジェクトを取得する処理を実装した\ ``CustomerSharedService``\ クラスをインジェクションして、DBから顧客情報を取得している。 .. code-block:: java public class ReservationUserDetailsService implements UserDetailsService { @Inject CustomerSharedService customerSharedService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Customer customer = customerSharedService.findOne(username); // omitted return new ReservationUserDetails(customer); } } 使用方法 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 作成した\ ``ReservationUserDetailsService``\ 、\ ``ReservationUserDetails``\ の使用方法を説明する。 * spring-security.xml .. code-block:: xml .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ``ReservationUserDetailsService``\ のBean IDをref属性に定義する。 * - | (2) - | \ ``ReservationUserDetailsService``\ をBean定義する。 * JSP \ ````\ タグを使用して\ ``Customer``\ オブジェクトにアクセスする。 .. code-block:: jsp ${f:h(customer.customerName)} .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ``ReservationUserDetails``\ がもつ\ ``Customer``\ オブジェクトを変数に格納する。 * - | (2) - | 変数に格納した\ ``Customer``\ オブジェクトの任意のプロパティを表示する。 | \ ``f:h()``\ については、\ :doc:`XSS`\ を参照されたい。 * Controller .. code-block:: java @RequestMapping(method = RequestMethod.GET) public String view(rincipal principal, Model model) { // get Authentication Authentication authentication = (Authentication) principal; // get UserDetails ReservationUserDetails userDetails = (ReservationUserDetails) authentication.getPrincipal(); // get Customer Customer customer = userDetails.getCustomer(); // (1) // omitted ... } .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ``ReservationUserDetails``\ から、ログイン中の\ ``Customer``\ オブジェクトを取得する。 | このオブジェクトをServiceクラスに渡して業務処理を行う。 .. note:: 顧客情報が変更された場合、一度ログアウトしないと\ ``ReservationUserDetails``\ がもつ\ ``Customer``\ オブジェクトは変更されない。 頻繁に変更されうる情報や、ログインユーザー以外のユーザー(管理者など)によって変更される情報は保持しない方がよい。 \ ``AuthenticationProvider``\ の拡張 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. todo:: 内容をもう一度見直す。 | Spring Securityで\ `デフォルトで用意されている認証プロバイダ `_\ で対応できない業務要件がある場合、 | \ ``org.springframework.security.authentication.AuthenticationProvider``\ を実装したクラスを作成する必要がある。 | \ ``AuthenticationProvider``\ では、\ ``org.springframework.security.core.Authentication``\ を実装したクラスを戻り値として返却する必要がある。 | \ ``Authentication``\ を実装したクラスには、\ ``getPrincipal``\ メソッド(認証情報の取得)、 | \ ``getCredentials``\ メソッド(principalの正当性を保証する情報の取得)を設定する必要がある。 | ここでは認証時に、ユーザ名、パスワード以外にも認証情報が必要な場合を考える。\ ``AuthenticationProvider``\ でこの値にアクセスするために、 * \ ``org.springframework.security.authentication.UsernamePasswordAuthenticationToken``\ * \ ``org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter``\ を継承したクラスを作成する必要がある。 | ユーザ名、\ **会社識別子**\ 、パスワードを認証情報とする場合の例を用いて、説明する。 .. figure:: ./images/Authentication_HowToExtends_LoginForm.png :alt: Authentication_HowToExtends_LoginForm :width: 40% \ ``UsernamePasswordAuthenticationToken``\ の拡張 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | \ ``UsernamePasswordAuthenticationToken``\ を継承した、独自の\ ``AuthenticationToken``\ を作成する。 | \ ``UsernamePasswordAuthenticationToken``\ を拡張することで、認証情報に会社識別子を持たせることができる。 .. code-block:: java // import omitted public class CompanyIdUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken { private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; private final String companyId; // (1) public CompanyIdUsernamePasswordAuthenticationToken( Object principal, Object credentials, String companyId) { // (2) super(principal, credentials); this.companyId = companyId; } public CompanyIdUsernamePasswordAuthenticationToken( Object principal, Object credentials, String companyId, Collection authorities) { // (3) super(principal, credentials, authorities); this.companyId = companyId; } public String getCompanyId() { return companyId; } } .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | 会社識別子用のフィールドを作成する。 * - | (2) - | 認証前に、\ ``CompanyIdUsernamePasswordAuthenticationToken``\ のインスタンスを作成する時に使用するコンストラクタ。 * - | (3) - | 認証成功後に、\ ``CompanyIdUsernamePasswordAuthenticationToken``\ のインスタンスを作成する時に使用するコンストラクタ。 | 親クラスのコンストラクタの引数に認可情報も併せて渡すことで、認証済みの状態となる。 独自\ ``AuthenticationProvider``\ の実装 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | 独自の\ ``AuthenticationProvider``\ で\ ``CompanyIdUsernamePasswordAuthenticationToken``\ を使用する。 .. code-block:: java // import omitted public class CompanyIdUsernamePasswordAuthenticationProvider implements AuthenticationProvider { // omitted @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { CompanyIdUsernamePasswordAuthenticationToken authenticationToken = (CompanyIdUsernamePasswordAuthenticationToken) authentication; // (1) // Obtain userName, password, companyId String username = authenticationToken.getName(); String password = authenticationToken.getCredentials().toString(); String companyId = authenticationToken.getCompanyId(); // Obtain user (and grants if needed) from database or other system // UserDetails userDetails = ... // Business logic // ... return new UsernamePasswordAuthenticationToken(userDetails, authentication.getCredentials(), Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"))); // (2) } @Override public boolean supports(Class authentication) { return CompanyIdUsernamePasswordAuthenticationToken.class .equals(authentication); // (3) } // omitted } .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ``UsernamePasswordAuthenticationFilter``\ を拡張したクラスで設定した、独自の\ ``AuthenticationToken``\ にキャストする。 | 詳細については、後述する\ :ref:`UsernamePasswordAuthenticationFilter の拡張`\ を参照されたい。 * - | (2) - | 認証処理(ユーザ情報の取得、パスワードチェック、会社識別子チェック等)実施後、 | \ ``UsernamePasswordAuthenticationToken``\ を作成して返却する。パラメータに認可情報も併せて設定することで、 | 認証済みの\ ``AuthenticationToken``\ インスタンスを生成する。\ ``UserDetails``\ もコンストラクタのパラメータに渡す。 | | 本例では、簡易実装として、単一のロールのみ使用している。 | 権限についての詳細は、\ :ref:`ユーザ情報管理クラスの設定 `\ を参照されたい。 * - | (3) - | 独自の\ ``AuthenticationToken``\ である、 | \ ``CompanyIdUsernamePasswordAuthenticationToken``\ クラスをサポート対象とする。 .. _authentication_custom_usernamepasswordauthenticationfilter: \ ``UsernamePasswordAuthenticationFilter``\ の拡張 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | \ ``UsernamePasswordAuthenticationFilter``\ を継承した、独自の\ ``AuthenticationFilter``\ を作成する。 | \ ``UsernamePasswordAuthenticationFilter``\ を拡張することで、\ ``CompanyIdUsernamePasswordAuthenticationProvider``\ へ、 | \ ``CompanyIdUsernamePasswordAuthenticationToken``\ を渡すことができる。 .. code-block:: java // import omitted public class CompanyIdUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter { @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (!request.getMethod().equals("POST")) { throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); } // Obtain UserName, Password, CompanyId String username = super.obtainUsername(request); String password = super.obtainPassword(request); String companyId = obtainCompanyId(request); // username required if (!StringUtils.hasText(username)) { throw new AuthenticationServiceException("UserName is required"); } // validate password, companyId // omitted other process CompanyIdUsernamePasswordAuthenticationToken authRequest = new CompanyIdUsernamePasswordAuthenticationToken(username, password, companyId); // (1) // Allow subclasses to set the "details" property setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); // (2) } protected String obtainCompanyId(HttpServletRequest request) { return request.getParameter("j_companyid"); // (3) } } .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ``HttpServletRequest``\ から取得した、ユーザ名、パスワード、会社識別子を設定した、 | \ ``CompanyIdUsernamePasswordAuthenticationToken``\ のインスタンスを生成する。 * - | (2) - | 未認証状態の\ ``CompanyIdUsernamePasswordAuthenticationToken``\ のインスタンスを | \ ``org.springframework.security.authentication.AuthenticationManager.authenticate``\ のパラメータとして設定する。 * - | (3) - | 会社識別子を、リクエストパラメータより取得する。 .. note:: **ログイン情報の入力チェックについて** DBサーバへの負荷軽減等で、あきらかな入力誤りに対しては、事前にチェックを行いたい場合がある。 その場合は、\ :ref:`authentication_custom_usernamepasswordauthenticationfilter`\ のように、 \ ``UsernamePasswordAuthenticationFilter``\ を拡張することで、入力チェック処理を行うことができる。 使用方法 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" * ログインフォームページ(JSP) \ :ref:`form-login-JSP`\ の例に、会社識別子を追加する。 .. code-block:: jsp :emphasize-lines: 7-8 User Id

Company Id

Password

.. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | 会社識別子の入力フィールドのnameは、j_companyid を指定する。 * spring-security.xml | 独自の\ ``AuthenticationProvider``\ 、\ ``AuthenticationFilter``\ を設定する。 .. code-block:: xml .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | custom-filter要素で、"FORM_LOGIN_FILTER" を差し替えた場合は、auto-config="true" を指定することができない。 | そのため、auto-configの指定を削除するか、auto-config="false" を指定する必要がある。 | また、form-login要素も指定できないため、entry-point-ref 属性を明示的に設定する必要がある。 * - | (2) - | custom-filter要素の position属性に "FORM_LOGIN_FILTER" を指定することで、 | UsernamePasswordAuthenticationFilterからCompanyIdUsernamePasswordAuthenticationFilterに差し替えることができる。 * - | (3) - | CompanyIdUsernamePasswordAuthenticationFilterをbean定義する。 * - | (4) - | authenticationManagerプロパティに、authentication-manager要素のalias属性の値を設定する。 * - | (5) - | authenticationFailureHandlerプロパティに、認証失敗時に呼ばれるハンドラクラスを指定する。 * - | (6) - | authenticationSuccessHandlerプロパティに、認証成功時に呼ばれるハンドラクラスを指定する。 * - | (7) - | filterProcessesUrlプロパティに、認証処理を行うパスを指定する。 * - | (8) - | http要素のentry-point-ref属性に指定する、AuthenticationEntryPointを定義する。 | form-login要素を指定した際のデフォルトのAuthenticationEntryPointである、 | \ ``org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint``\ をbean定義する。 * - | (9) - | コンストラクタの引数に、ログイン画面のパスを指定する。 * - | (10) - | authentication-provider要素にCompanyIdUsernamePasswordAuthenticationProvider を参照設定する。 * - | (11) - | CompanyIdUsernamePasswordAuthenticationProviderを、bean定義する。 .. warning:: auto-config="false" を指定したことで、\ ````\ 、\ ````\ 要素を使用する場合は、明示的に定義する必要がある。 Appendix -------------------------------------------------------------------------------- | Spring Securityを使用した認証では、認証に成功した場合設定ファイルに記述したパスに遷移する。 | 「続きを読むにはログインする必要がある」のような業務用件がある場合、 | ログイン後の遷移先を動的に変更したい場合がある。 .. figure:: ./images/Authentication_Appendix_ScreenFlow.png :alt: Authentication_Appendix_Screen_Flow :width: 60% :align: center **Picture - Screen_Flow** | そのような場合、共通ライブラリで提供している、 | \ ``org.terasoluna.gfw.web.security.RedirectAuthenticationHandler``\ を使用する。 | RedirectAuthenticationHandlerを使用した設定例を、下記に示す。 **遷移元画面のJSPの記述例** .. code-block:: jsp .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | リダイレクトURLの設定 | hidden項目の「redirectTo」に遷移先のURLを設定する。 | nameに指定する値は、後述する設定ファイルに記載する、 | targetUrlParameterと一致させること。 **ログイン画面のJSPの記述例** .. code-block:: jsp .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | リダイレクトURLの設定 | 遷移元画面からリクエストパラメータで渡された、 | リダイレクトURLをhidden項目に設定する。 **Spring Security 設定ファイル** .. code-block:: xml .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | authentication-failure-handler-ref(認証エラー時のハンドラ設定)と | authentication-success-handler-ref(認証成功時のハンドラ設定)のBeanIdを指定する。 * - | (2) - | authentication-success-handler-refの参照クラスとして | \ ``org.terasoluna.gfw.web.security.RedirectAuthenticationHandler``\ を設定する。 * - | (3) - | 前述したJSPのhiddenに記載した値と一致させること。 | 本例では、「redirectTo」を指定する。省略した場合、「redirectTo」が設定される。 * - | (4) - | authentication-failure-handler-refの参照クラスとして | \ ``org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler``\ を設定する。 * - | (5) - | 認証失敗時の遷移先パスにはログイン画面のパス、エラーを示すクエリを設定する。 * - | (6) - | 本機能を使用する場合はuseForwardをtrueに指定する必要がある。 | trueに指定することで、redirectでの遷移から、forwardでの遷移となる。 | リクエストパラメータにリダイレクト先のURLを保持しているため、 | forwardにすることでリクエストパラメータを保持したままにする必要がある。 .. tip:: RedirectAuthenticationHandlerは、オープンリダイレクタ脆弱性対策が施されているため、 拡張せずに「http://google.com」のような、外部サイトへの遷移をすることはできない。 別ドメインに移動したいときは、\ ``org.springframework.security.web.RedirectStrategy``\ を実装したクラスを作成する必要がある。 RedirectStrategyを実装したクラスは、セッターインジェクションでRedirectAuthenticationHandlerのtargetUrlParameterRedirectStrategyに設定する。 拡張する際の注意点としては、redirectToの値を改竄されても問題ない作りにする必要がある。 たとえば、リダイレクト先のURLを直接指定せず番号指定にする(ページ番号指定)、 リダイレクト先のドメインをチェックする等のいずれか対応が必要となる。