Java Authentication and Authorization Service (JAAS): LoginModule開発者ガイド
JAASは、認証されたアイデンティティにおけるサブジェクトベースの認証を提供します。このドキュメントでは、JAASの認証面、特にLoginModuleインタフェースに焦点を当てて解説します。
このドキュメントの対象読者
このドキュメントは、認証技術を実装するLoginModuleを記述する必要がある、経験を積んだプログラマ向けに書かれています。
関連ドキュメント
このドキュメントでは、読者がすでに次のドキュメントを読んでいることを前提としています。
JAAS APIのさまざまなクラスおよびインタフェースについての説明も含まれます。詳細は、JAAS API仕様のJavaDoc APIドキュメントを参照してください:
- javax.security.auth
- javax.security.auth.callback
- javax.security.auth.kerberos
- javax.security.auth.login
- javax.security.auth.spi
- javax.security.auth.x500
次のパッケージには、サポートされるLoginModuleの例が含まれています。
- com.sun.security.auth
- com.sun.security.auth.callback
- com.sun.security.auth.login
- com.sun.security.auth.module
次のチュートリアルは、JAAS認証および承認を利用するすべてのユーザーを対象としています。
次のチュートリアルは、JAAS認証および承認のチュートリアルと似ていますが、Kerberos LoginModuleの使用方法の解説が含まれるため、使用する前にKerberosをインストールする必要があります。
この2つのチュートリアルは、認証とセキュアな通信のための基盤技術としてKerberosを利用するJAASおよびJava GSS-APIチュートリアルの紹介に含まれています。
LoginModuleの概要
認証テクノロジ・プロバイダはLoginModuleインタフェースを実装する必要があります。LoginModule
は特定タイプの認証を提供するアプリケーションへプラグインされます。
アプリケーションはLoginContextアプリケーション・プログラミング・インタフェース(API)への書込みを実行するのに対し、認証技術のプロバイダはLoginModule
インタフェースを実装します。Configurationでは、特定のログイン・アプリケーションで使用されるLoginModule
を指定します。アプリケーション自体を変更せずに、複数の異なるLoginModule
をアプリケーションのプラグインとして使用できます。
LoginContext
は、Configuration
の読取りと特定のLoginModule
のインスタンス化を行います。各LoginModule
はSubject、CallbackHandler、共有のLoginModule
の状態、およびLoginModule
固有のオプションを使用してインスタンス化されます。
Subject
は、認証中のユーザーまたはサービスを表し、認証が成功すると、関連するPrincipalおよびクレデンシャルを保持するLoginModule
により更新されます。LoginModule
は、loginメソッドの説明で述べるように、CallbackHandler
を使用してユーザーと通信します(ユーザー名とパスワードの入力を求めるためなど)。CallbackHandler
はnullも指定できることに注意してください。Subject
の認証にCallbackHandler
を必要とするLoginModule
は、null
のCallbackHandler
で初期化されていると、LoginExceptionをスローする場合があります。LoginModule
はオプションで共有状態を使用して、それら自体の間で情報やデータを共有できます。
LoginModule
固有のオプションは、ログインConfiguration
内で、このLoginModule
用に構成されたオプションを表します。これらのオプションはLoginModule
自体によって定義され、その中での動作を制御します。たとえば、LoginModule
でデバッグ/テスト機能をサポートするオプションを定義する場合を考えましょう。オプションは、debug=trueなどの、キーと値の構文を使用して定義されます。キーを使用して値を取得できるように、LoginModule
はオプションをMap
として格納します。LoginModule
が定義することを選択するオプションの数に制限はありません。
呼出し元アプリケーションは、認証プロセスをLoginContext
のlogin
メソッド呼出しによって呼び出された単一の操作であると見なします。ただし、各LoginModule
内部の認証プロセスは、明確な2つのフェーズにわかれています。認証の最初のフェーズでは、LoginContext
のlogin
メソッドにより、Configuration
内に指定された各LoginModule
のlogin
メソッドが呼び出されます。LoginModule
のlogin
メソッドは、実際の認証を実行してから(たとえば、パスワードの入力を促し、入力されたパスワードを検証する)、認証ステータスを非公開の状態情報として保存します。LoginModule
のlogin
メソッドは終了後に、true
(成功した場合)またはfalse
(無視する場合)を返すか、LoginException
をスローして障害を指定します。障害が発生した場合、LoginModule
は認証を再試行したり、遅延を招いたりしてはいけません。これらのタスクの実行は、アプリケーションが担当します。アプリケーションが認証の再実行を試みると、各LoginModule
のlogin
が再度呼び出されます。
次のフェーズでは、LoginContext
の認証がすべて成功した(関連するrequired、requisite、sufficientおよびoptional LoginModule
のlogin
メソッドの呼出しに成功した)場合、各LoginModule
のcommit
メソッドが呼び出されます。(LoginModule
フラグ(required、requisite、sufficientおよびoptional)については、Configurationのドキュメント、およびJAASリファレンス・ガイドの付録B「JAASログイン構成ファイル」を参照してください。)LoginModule
のcommit
メソッドは、非公開で保存された状態をチェックして、独自の認証が成功したかどうかを確認します。LoginContext
の認証全体が成功し、LoginModule
自体の認証が成功すると、commit
メソッドにより、関連するPrincipal
(認証済みの識別情報)およびクレデンシャル(暗号化キーなどの認証データ)がSubject
に関連付けられます。
LoginContext
の認証全体が失敗した(関連するREQUIRED、REQUISITE、SUFFICIENTおよびOPTIONAL LoginModule
のlogin
メソッドが成功しなかった)場合、各LoginModule
のabort
メソッドが呼び出されます。この場合、LoginModule
は、当初保存されていた認証状態をすべて削除/破棄します。
Subject
からのログアウトには、1つのフェーズのみが含まれます。LoginContext
は、LoginModule
のlogout
メソッドを呼び出します。その後、LoginModule
のlogout
メソッドは、Subject
からのPrincipal
またはクレデンシャルの削除や、セッション情報のロギングなど、ログアウト処理を実行します。
LoginModuleの実装ステップ
次は、LoginModule
の実装およびテストに必要なステップです。
ステップ2: LoginModule実装への命名
LoginModule
の適切なパッケージおよびクラス名を決定します。
たとえば、IBMにより開発されたLoginModule
にはcom.ibm.auth.Module
と命名できます。ここで、com.ibm.auth
はパッケージ名、Module
はLoginModule
クラス実装の名前です。
ステップ3: LoginModuleインタフェースの実装
LoginModule
インタフェースは、実装する必要のある5つの抽象メソッドを指定します。
LoginModule実装は、これらのメソッドに加え、引数をとらないpublicのコンストラクタを提供する必要があります。このことにより、LoginContextによるインスタンス化が適切に行われるようになります。LoginModule実装でこの種のコンストラクタが提供されない場合、引数を取らないデフォルト・コンストラクタが自動的にObjectクラスから継承されます。
ノート:
LoginModuleインタフェースを実装しない場合は、ログイン・モジュールを使用しようとするとLoginExceptionがスローされます。LoginModule.initializeメソッド
public void initialize(
Subject subject,
CallbackHandler handler,
Map<java.lang.String, ?> sharedState,
Map<java.lang.String, ?> options);
initialize
メソッドが呼び出され、関連する認証および状態情報でLoginModule
の初期化が行われます。
このメソッドは、LoginModule
をインスタンス化したあと(かつほかのpublicメソッドを呼び出す前)、すぐにLoginContext
により呼び出されます。このメソッド実装は、将来の使用に備えて指定された引数を格納します。
initialize
メソッドはさらに、指定されたsharedStateを調べて、他のLoginModule
から提供された追加の認証状態を特定し、指定されたoptionsも詳しく調べて、LoginModule
の動作に影響を与えるように指定された構成オプションを特定します。将来の使用に備えて、変数内にオプションの値を保存することもできます。
次に示すのは、LoginModuleがサポートする一般的なオプションの一覧です。ただし、これはガイドラインにすぎません。モジュールは、次のオプションのサブセットを随意サポートします。
tryFirstPass
-true
の場合、スタック内の最初のLoginModuleが入力されたパスワードを保存し、それ以降のLoginModulesもこれを使用することを試みます。認証に失敗した場合、LoginModuleは新しいパスワードを要求し、認証を再試行します。useFirstPass
-true
の場合、スタック内の最初のLoginModuleが入力されたパスワードを保存し、それ以降のLoginModulesもこれを使用することを試みます。LoginModuleは、認証に失敗しても新しいパスワードを要求しません。tryMappedPass
-true
の場合、スタック内の最初のLoginModuleが入力されたパスワードを保存し、それ以降のLoginModulesはこれをサービス固有のパスワードにマッピングすることを試みます。認証に失敗した場合、LoginModuleは新しいパスワードを要求し、認証を再試行します。useMappedPass
-true
の場合、スタック内の最初のLoginModuleが入力されたパスワードを保存し、それ以降のLoginModulesはこれをサービス固有のパスワードにマッピングすることを試みます。LoginModuleは、認証に失敗しても新しいパスワードを要求しません。moduleBanner
-true
の場合、LoginModuleは、CallbackHandlerの呼出し時に最初のCallbackとしてTextOutputCallbackを提供します。このCallbackには、認証を実行しているLoginModuleに関する情報が記述されます。debug
-true
の場合、LoginModuleに対してデバッグ情報の出力を指示します。
initialize
メソッドは、認識できない状態やオプションを無視できます。ただし、この種のイベントが発生した場合、それを記録する方が望ましい場合もあります。
このLoginModule
(およびその他の構成済LoginModule
)を呼び出すLoginContext
はすべて、指定されたSubject
およびsharedState
への同一の参照を共有します。Subject
およびsharedState
への変更は、すべてが認識します。
LoginModule.loginメソッド
boolean login() throws LoginException;
login
メソッドが呼び出され、Subject
の認証が行われます。これが認証の第1フェーズです。
このメソッド実装は、実際の認証を実行します。たとえば、ユーザー名およびパスワードの入力を求めてから、パスワード・データベースに対しパスワードの検証を試みます。別のサンプル実装として、フィンガプリント・リーダーに指を挿入するようユーザーに指示し、入力されたフィンガ・プリントをフィンガプリント・データベースと照合する場合が考えられます。
LoginModule
がなんらかのユーザーとの通信の形式を必要とする(ユーザー名とパスワードの取得など)場合は、それを直接実行しないでください。それは、ユーザーとの様々な通信方法が存在するためであり、LoginModule
が様々なユーザーとの通信のタイプから独立させておくことが望ましいです。それよりも、LoginModule
のlogin
メソッドから、initialize
メソッドに渡されたCallbackHandlerインタフェースのhandle
メソッドを呼び出して、ユーザーと通信し、適切な結果(ユーザー名、パスワードなど)を設定するようにしてください。LoginModule
はCallbackHandler
に適切なCallback
(ユーザー名に対してはNameCallback 、パスワードに対してはPasswordCallback )からなる配列を渡し、CallbackHandler
は要求に従ってユーザーと通信し、Callback
内に適切な値を設定します。たとえば、NameCallback
を処理する場合、CallbackHandler
はユーザーから名前を取得し、NameCallback
のsetName
メソッドを呼び出してその名前を格納します。
認証プロセスに、ネットワーク経由の通信が含まれる場合もあります。たとえば、このメソッド実装がKerberosのkinitと等価な機能を実行する場合、KDCとのコンタクトが必要になります。パスワード・データベースのエントリがリモート・ネーム・サービス内に存在する場合、Java Naming and Directory Interface (JNDI)を利用するなどして、そのネーム・サービスとコンタクトを取る必要があります。実装は、基盤となるオペレーティング・システムと通信する必要もあります。たとえば、ユーザーがLinux、macOS、Windowsなどのオペレーティング・システムにすでにログインしている場合、このメソッドは基盤となるオペレーティング・システムの識別情報を単にインポートします。
login
メソッドは、次の処理を行う必要があります。
- この
LoginModule
を無視するかどうかを決定します。たとえば、ユーザーがこのLoginModule
と無関係な識別情報を使って認証を試みた場合(たとえば、ユーザーがNISを使い、rootで認証を試みた場合)は、LoginModuleを無視する必要があります。このLoginModule
を無視する必要がある場合、login
の戻り値はfalse
になります。それ以外の場合、次の処理を行います。 - ユーザーとの通信が必要な場合は
CallbackHandler
のhandle
メソッドを呼び出します。 - 認証を実行します。
- 認証結果(成功または失敗)を格納します。
- 認証に成功した場合は、関連状態情報をすべて保存します。この情報は、
commit
メソッドで使用される可能性があります。 - 認証に成功した場合は
true
を返し、認証に失敗した場合はLoginException (FailedLoginExceptionなど)をスローします。
login
メソッド実装が、新規Principal
またはクレデンシャル情報を保存済みのSubject
オブジェクトに関連付けることはできません。このメソッドは、認証のみを実行して、認証結果および対応する認証状態を格納します。あとで、commit
またはabort
メソッドからこの結果および状態へのアクセスが行われます。結果および状態は、ほかのLoginModule
と共有ではないので、通常、sharedState Map
には保存できません。
たとえば、LoginModule
がパスワードを共有するように構成されている場合であれば、このメソッドにとって、sharedStateのMap
に状態情報を保存することは有利です。この場合、入力されたパスワードは、共有状態として保存されます。パスワードの共有により、パスワードを1回入力するだけで後続のLoginModule
でも認証された状態を維持できます。次に、sharedStateのMap
から名前とパスワードを格納および保存する際の標準的な規約を示します。
javax.security.auth.login.name
- 名前を保存/取得するための共有状態マップ・キーとして使用。値はStringである必要があります。javax.security.auth.login.password
- パスワードを保存/取得するための共有状態マップ・キーとして使用。値はchar配列である必要があります。
認証に失敗した場合、login
メソッドは認証を再試行できません。この処理はアプリケーションが担当します。LoginModule.login()
内から何度もログインを試みるよりも、アプリケーションから繰り返しLoginContext
のlogin
メソッドを呼び出すことをお薦めします。
LoginModule.commitメソッド
boolean commit() throws LoginException;
commit
メソッドが呼び出され、認証プロセスがコミットされます。これは、第1認証フェーズが成功した場合の第2認証フェーズです。LoginContext
全体の認証が成功した場合(関連するREQUIRED、REQUISITE、SUFFICIENT、およびOPTIONALのLoginModule
が成功した場合)に呼び出されます。
このメソッドは、login
メソッドにより保存された認証結果および対応する認証状態にアクセスします。
認証結果からlogin
メソッドの失敗が明らかになった場合、commit
メソッドは最初に保存された対応する状態をすべて削除/破棄します。
保存された結果からLoginModule
のlogin
メソッドの成功が明らかになった場合は、対応する状態情報から関連するPrincipal
およびクレデンシャルの情報が構築されます。このPrincipal
およびクレデンシャルの情報が、initialize
メソッドによって格納されたSubject
に追加されます。
Principal
およびクレデンシャルの追加後は、不要な状態フィールドを迅速に破棄する必要があります。たとえば、認証プロセスで格納されたユーザー名やパスワードは破棄されます。
commit
メソッドは、コミットの成功または失敗を示す非公開の状態を保存する必要があります。
次に、LoginModule
のcommit
メソッドから返される結果を図示します。各ボックスは、発生する可能性がある各種の状況を表します。たとえば、上部左側のボックスは、以前のlogin
メソッド呼出しとcommit
メソッド自体の呼出しに成功した場合に返されるcommit
メソッドの結果を示しています。
表6-2 LoginModule.commitメソッドの戻り値
ログイン・ステータス | COMMIT: 成功 | COMMIT: 失敗 |
---|---|---|
LOGIN: 成功 | TRUEを返す | 例外をスローする |
LOGIN: 失敗 | FALSEを返す | FALSEを返す |
LoginModule.abortメソッド
boolean abort() throws LoginException;
abort
メソッドが呼び出され、認証プロセスが強制的に中断されます。これは、第1認証フェーズが失敗した場合の第2認証フェーズです。これは、LoginContext
の全体の認証に失敗した場合に呼び出されます。
このメソッドは、最初に、login
メソッド(および場合によりcommit
メソッド)によって保存されているLoginModule
の認証結果および対応する認証状態にアクセスし、情報をクリアおよび破棄します。たとえば、ユーザー名およびパスワードは破棄されます。
LoginModule
の認証に失敗した場合、非公開の状態を消去する必要は生じません。
次に、LoginModule
のabort
メソッドから返される結果を図示します。最初の図は、以前のlogin
呼出しが成功したことを想定しています。たとえば、abort
メソッドは、以前のlogin
呼出しとcommit
呼出しに成功し、さらにabort
メソッド自体も成功した場合は、TRUEを返します。
表6-3 ログイン成功時のLoginModule.abortメソッドの戻り値
ログイン・ステータス | ABORT: 成功 | ABORT: 失敗 |
---|---|---|
COMMIT: 成功 | TRUEを返す | 例外をスローする |
COMMIT: 失敗 | TRUEを返す | 例外をスローする |
2番目の図は、以前のloginメソッドの呼出しに失敗した場合に返されるLoginModuleのabortメソッドの結果を示しています。たとえば、abortメソッドは、以前のlogin呼出しには失敗し、以前のcommit呼出しには成功し、さらにabortメソッド自体も成功した場合は、FALSEを返します。
表6-4 ログイン失敗時のLoginModule.abortメソッドの戻り値
ログイン・ステータス | ABORT: 成功 | ABORT: 失敗 |
---|---|---|
COMMIT: 成功 | FALSEを返す | FALSEを返す |
COMMIT: 失敗 | FALSEを返す | FALSEを返す |
LoginModule.logoutメソッド
boolean logout() throws LoginException;
logout
メソッドが呼び出され、Subject
からログアウトします。
このメソッドは、Principal
を削除し、commit
操作中にSubject
に関連付けられたクレデンシャルを削除/破棄します。このメソッドは、Subject
内の既存のPrincipal
やクレデンシャル、またはほかのLoginModule
によって追加されたPrincipalやクレデンシャルに対しては、一切の操作を実行できません。
Subject
が読取り専用 (Subject
のisReadOnly
メソッドがtrueを返す)の場合、このメソッドはcommit
操作中にSubject
に関連付けられているクレデンシャルのみを破棄します(クレデンシャルを削除することはできません)。Subject
が読取り専用であり、commit
操作中にSubject
と関連付けられたクレデンシャルを破棄できない(このクレデンシャルがDestroyable
インタフェースを実装していない)場合、このメソッドはLoginException
をスローする可能性があります。
logout
メソッドは、ログアウトに成功した場合はtrue
を返し、それ以外の場合はLoginException
をスローします。
ステップ4: サンプル・アプリケーションの選択または記述
テスト用として、既存のサンプル・アプリケーションを選択するか、新しいサンプル・アプリケーションを作成します。
アプリケーション要件、およびテストに使用できるサンプル・アプリケーションについては、Java Authentication and Authorization Service (JAAS)リファレンス・ガイドを参照してください。
ステップ6: テストの準備
ステップ6a: LoginModule
とアプリケーション・コードをJARファイルに配置
ステップ6b: LoginModuleとアプリケーションのJARファイルにアクセス権を設定でポリシー内のJARファイルを参照できるように、LoginModule
およびアプリケーション・コードを別々のJARファイルに格納します。次は、JARファイルを作成するサンプル・コマンドです。
jar cvf <JAR file name> <list of classes, separated by spaces>
このコマンドにより、指定されたクラスを含む、指定された名前のJARファイルが作成されます。
jarツールの詳細は、jar
を参照してください。
ステップ6b: LoginModule
とアプリケーションのJARファイルにアクセス権を設定
LoginModule
やアプリケーションが、セキュリティ・チェックをトリガーするセキュリティ関連タスク(ネットワーク接続の確立、ローカル・ディスク上のファイルの読取り、書込みなど)を実行するとします。セキュリティ・マネージャがインストールされている環境で実行する場合は、必要なアクセス権を付与する必要があります(JDKでのアクセス権を参照)。
警告:
セキュリティ・マネージャおよびそれに関連するAPIは非推奨であり、今後のリリースでは削除されます。セキュリティ・マネージャの代わりとなるものはありません。詳細および代替手段については、JEP 411を参照してください。通常、LoginModule
はPrincipal
とクレデンシャルを認証済サブジェクトに関連付けます。このため、LoginModule
は、アクセス権として、modifyPrincipals、modifyPublicCredentialsおよびmodifyPrivateCredentialsというターゲット名のAuthPermissionを必要とします。
次に示すサンプル文は、コードがMyLM.jar
に記述されているLoginModule
へのアクセス権を付与するものです。この種の文は、ポリシー・ファイルに記述されます。この例では、MyLM.jar
ファイルは/localWork
ディレクトリに格納されるものとします。
grant codeBase "file:/localWork/MyLM.jar" {
permission javax.security.auth.AuthPermission "modifyPrincipals";
permission javax.security.auth.AuthPermission "modifyPublicCredentials";
permission javax.security.auth.AuthPermission "modifyPrivateCredentials";
};
ノート:
LoginModule
呼出しは、常にAccessController.doPrivileged
呼出し内で行われるため、doPrivileged
自体を呼び出す必要はありません。これを呼び出すと、意図せずにセキュリティ・ホールを作り出してしまう可能性があります。たとえば、LoginModule
がdoPrivileged
呼出しの中でアプリケーション提供のCallbackHandler
を呼び出す場合、他の場合にはアクセス不可能なリソースへのアクセスがアプリケーションのCallbackHandler
に許可されるため、セキュリティ・ホールが生まれます。
ステップ6c: LoginModuleを参照する構成を作成
JAASはプラガブルな認証アーキテクチャをサポートするため、既存のアプリケーションを変更せずに新しいLoginModule
を使用できます。新しいLoginModule
の使用を示すためには、ログインConfiguration
を更新するだけで済みます。
OracleのデフォルトのConfiguration
実装は、ConfigFileで説明するように、構成ファイルから構成情報を読み取ります。
テスト用の構成ファイルを作成します。たとえば、以前取り上げた架空のIBMのアプリケーション用LoginModule
を構成する場合、次のような内容の構成ファイルを使用することになります。
AppName {
com.ibm.auth.Module REQUIRED debug=true;
};
AppName
は、アプリケーションがログイン構成ファイル内のこのエントリを参照するために使用する任意の名前です。アプリケーションはこの名前をLoginContext
コンストラクタの第1引数として指定します。
ステップ7: LoginModuleの試用
アプリケーション、およびそれによるLoginModule
の使用をテストします。アプリケーションの実行時、使用するログイン構成ファイルを指定します。たとえば、MyApp.jar
にMyApp
という名前のアプリケーションが格納されていて、構成ファイルの名前がtest.conf
だとします。
この場合、次のようにしてアプリケーションを実行し、構成ファイルを指定できます。
java -classpath MyApp.jar
-Djava.security.auth.login.config=test.conf MyApp
コマンド全体は、1行で入力してください。ここでは、読みやすくするために複数行に分けて表示してあります。
my.policy
という名前のポリシー・ファイルを指定し、インストールされているセキュリティ・マネージャを使ってアプリケーションを実行するには、次のように入力します。
java -classpath MyApp.jar -Djava.security.manager
-Djava.security.policy=my.policy
-Djava.security.auth.login.config=test.conf MyApp
ここでも、コマンド全体は1行で入力してください。
警告:
セキュリティ・マネージャおよびそれに関連するAPIは非推奨であり、今後のリリースでは削除されます。セキュリティ・マネージャの代わりとなるものはありません。詳細および代替手段については、JEP 411を参照してください。debugオプションを付けてLoginModule
を構成すると、適正に動作することを確認できます。
コードをデバッグし、必要に応じてテストを続行します。問題が発生する場合は、ここまでのすべてのステップが完了していることを確認してください。
ユーザーによる入力と、構成ファイルに指定したLoginModule
オプションを確実に変更してください。
異なったインストール・オプション(LoginModule
をクラス・パスやモジュール・パス内に配置するなど)および実行環境(セキュリティ・マネージャを実行する、または実行しない)を使用するテストも実行してください。特に、セキュリティ・マネージャがインストールされている場合にLoginModule
を確実に動作させるには、ステップ6b: LoginModuleとアプリケーションのJARファイルにアクセス権を設定で説明されているように、必要なアクセス権を付与した後、インストールおよび実行環境をテストする必要があります。
- テスト中に、
LoginModule
またはアプリケーションに変更が必要なことが判明した場合は、変更を行い、再コンパイルします(ステップ5: LoginModuleおよびアプリケーションのコンパイル)。 - 更新したコードをJARファイルに配置します(ステップ6: JARファイルへのLoginModuleおよびアプリケーション・コードの記述)。
- 必要な場合は、アクセス権を修正するか追加します(ステップ6b: LoginModuleとアプリケーションのJARファイルにアクセス権を設定)。
- 必要な場合は、ログイン構成ファイルを変更します(ステップ6c: LoginModuleを参照する構成を作成)。
- アプリケーションを再実行し、必要に応じてこれらのステップを繰り返します。