当初、Java 認証・承認サービス (Java Authentication and Authorization Service: JAAS) は、Java 2 SDK, Standard Edition (J2SDK), v 1.3 のオプションパッケージでした。JAAS は、J2SDK 1.4 より Java Standard Edition Development Kit に統合されています。
JAAS は、次の 2 つの目的で使用できます。
このセクションでは、認証コンポーネントの基本的なチュートリアルを提供します。承認コンポーネントについては、「JAAS 承認」チュートリアルで説明します。
JAAS 認証は、プラグイン可能方式で実行されます。つまり、Java アプリケーションは、基盤となる認証技術から独立して機能します。新規または更新された技術を、アプリケーション自体を変更せずに、プラグインとして使用可能です。使用する特定の認証技術の実装は実行時に決定されます。その内容は、ログイン構成ファイルに指定します。このチュートリアルで使用する認証技術は、ユーザーが特定の名前とパスワードを指定しているかどうかを確認するだけの非常に基本的なものです。
このチュートリアルは、次のセクションで構成されます。
チュートリアルのコードを最初に実行してみる場合は、「コードの実行」を先に読んでから、その他のセクションに戻り、コードの記述および構成ファイルの詳細について学習してください。
このチュートリアルのコードは、3 つのファイルで構成されます。
認証チュートリアルのアプリケーションコードは、単一のソースファイル SampleAcn.java に含まれています。このファイルには次の 2 つのクラスが含まれています。
main
メソッドは、認証の実行後に、認証が成功したかどうかをレポートします。
ユーザーの認証用コードは、非常に簡潔です。次の 2 つのステップで構成されています。
最初に基本的なコードを紹介します。続いて、必要な import 文およびエラー処理を含む SampleAcn
ソースファイルの完全なコードを示します。
ユーザーの認証には、まず javax.security.auth.login.LoginContext
が必要です。次に、LoginContext をインスタンス化する基本的な方法を示します。
import javax.security.auth.login.*; . . . LoginContext lc = new LoginContext(<config file entry name>, <CallbackHandler to be used for user interaction>);次に、このチュートリアルコードによるインスタンス化の実行方法を具体的に示します。
import javax.security.auth.login.*; . . . LoginContext lc = new LoginContext("Sample", new MyCallbackHandler());
引数について、次に説明します。
これは、LoginContext が、JAAS ログイン構成ファイル内でこのアプリケーションのエントリ検索に使用する名前です。詳細は、ここを参照してください。このようなエントリは、基盤となる適切な認証技術を実装するクラスを指定します。クラスは、javax.security.auth.spi
パッケージ内の LoginModule インタフェースを実装する必要があります。
このサンプルコードでは、このチュートリアルに付属している SampleLoginModule
を使用します。SampleLoginModule は、ユーザーが特定の名前およびパスワードを入力したかどうかを確認することによって認証を実行します。
このチュートリアルで使用するログイン構成ファイル (sample_jaas.config) のエントリは、「Sample」という名前です。LoginContext コンストラクタの最初の引数には、この名前を指定してください。
LoginModule がユーザーと通信する必要がある場合 (たとえばユーザー名やパスワードの入力を求める場合)、通信は直接には行われません。このため、ユーザーとのさまざまな通信方法が存在します。実際のところ、ログインモジュールがユーザーと通信する際、特定の方法に依存しないようにすることは、望ましい方法です。LoginModule は、javax.security.auth.callback.CallbackHandler
を呼び出してユーザーと通信し、ユーザー名、パスワードをはじめとする要求された情報を取得します。
使用する特定の CallbackHandler のインスタンスを、LoginContext コンストラクタの 2 番目の引数として指定します。LoginContext は、このインスタンスを基盤となるログインモジュール (ここでは SampleLoginModule) に転送します。通常、アプリケーションは、固有の CallbackHandler 実装を提供します。com.sun.security.auth.callback
パッケージには、サンプル実装として 2 つの単純な CallbackHandler (TextCallbackHandler と DialogCallbackHandler) が用意されています。このチュートリアルでは、コマンド行に情報を出力し、コマンド行から入力を読み取る TextCallbackHandler を使用できます。ただし、実際に紹介するのは、固有の CallbackHandler 実装を提供するアプリケーションの典型例です (「MyCallbackHandler クラス」を参照)。
login
メソッドの呼び出しこれで LoginContext lc
を保持できたので、login
メソッドを呼び出して認証処理を実行します。
lc.login();
LoginContext は、新しい空の javax.security.auth.Subject
オブジェクト (認証されるユーザーまたはサービスを表す) をインスタンス化します。また、構成済みのログインモジュール (この例の場合は SampleLoginModule) を構築し、この新しいサブジェクトと MyCallbackHandler を使ってインスタンス化します。
次に、LoginContext の login
メソッドによって SampleLoginModule 内のメソッドが呼び出され、ログインおよび認証が実行されます。SampleLoginModule は、MyCallbackHandler を使ってユーザー名とパスワードを取得します。次に、名前とパスワードが適切であるかどうかをチェックします。
認証に成功した場合、SampleLoginModule は、ユーザーを表すプリンシパルをサブジェクトに追加します。SampleLoginModule によってサブジェクトに追加されるプリンシパルは、SamplePrincipal (java.security.Principal インタフェースを実装するサンプルクラス) のインスタンスです。
これらの用語の詳細は、「サブジェクト、プリンシパル、認証、およびクレデンシャル」を参照してください。
そのあと、呼び出し側アプリケーションは、LoginContext の getSubject
メソッドを呼び出し (このステップは、このチュートリアルではオプション)、認証されたサブジェクトを取得します。
SampleAcn
クラスのコードここまでで、ユーザーの認証に必要な基本コードを見てきました。ここで、すべてを統合して、関連する import 文およびエラー処理を含む完全なクラスを SampleAcn.java 内に作成できます。
package sample; import javax.security.auth.login.*; // . . . other import statements needed by MyCallbackHandler . . . /** * This Sample application attempts to authenticate a user * and reports whether or not the authentication was * successful. */ public class SampleAcn { /** * Attempt to authenticate the user. * * @param args input arguments for this application. * These are ignored. */ public static void main(String[] args) { // Obtain a LoginContext, needed for authentication. // Tell it to use the LoginModule implementation // specified by the entry named "Sample" in the // JAAS login configuration file and to also use the // specified CallbackHandler. LoginContext lc = null; try { lc = new LoginContext("Sample", new MyCallbackHandler()); } catch (LoginException le) { System.err.println("Cannot create LoginContext. " + le.getMessage()); System.exit(-1); } catch (SecurityException se) { System.err.println("Cannot create LoginContext. " + se.getMessage()); System.exit(-1); } // the user has 3 attempts to authenticate successfully int i; for (i = 0; i < 3; i++) { try { // attempt authentication lc.login(); // if we return with no exception, // authentication succeeded break; } catch (LoginException le) { System.err.println("Authentication failed:"); System.err.println(" " + le.getMessage()); try { Thread.currentThread().sleep(3000); } catch (Exception e) { // ignore } } } // did they fail three times? if (i == 3) { System.out.println("Sorry"); System.exit(-1); } System.out.println("Authentication succeeded!"); } }
ログインモジュールがユーザーと通信を行なって、認証情報を取得することが必要な場合があります。ログインモジュールは javax.security.auth.callback.CallbackHandler
を使用してこれを実行します。アプリケーションは、com.sun.security.auth.callback
パッケージ内のサンプル実装を作成できます。一般的には、CallbackHandler 実装を作成することもできます。アプリケーションは、LoginContext のインスタンス化の引数として CallbackHandler を渡します。LoginContext は、この CallbackHandler を基盤となるログインモジュールに直接転送します。
このチュートリアルのサンプルコードでは、独自の CallbackHandler 実装 (SampleAcn.java 内の MyCallbackHandler クラス) を使用します。
CallbackHandler は、1 つのメソッドを実装するインタフェースです。
void handle(Callback[] callbacks) throws java.io.IOException, UnsupportedCallbackException;
このログインモジュールは、CallbackHandler の handle メソッドに適切な javax.security.auth.callback.Callback
の配列 (ユーザー名の場合 NameCallback、パスワードの場合 PasswordCallback) を渡します。CallbackHandler は、要求に従ってユーザーと通信し、Callback 内に適切な値を設定します。
MyCallbackHandler の handle
メソッドの構築方法は次のとおりです。
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (int i = 0; i < callbacks.length; i++) { if (callbacks[i] instanceof TextOutputCallback) { // display a message according to a specified type . . . } else if (callbacks[i] instanceof NameCallback) { // prompt the user for a username . . . } else if (callbacks[i] instanceof PasswordCallback) { // prompt the user for a password . . . } else { throw new UnsupportedCallbackException (callbacks[i], "Unrecognized Callback"); } } }
CallbackHandler の handle
メソッドは、特定の型の Callback インスタンス (NameCallback、PasswordCallback など) の配列を受け取ります。このメソッドは、アプリケーションの実行に適した方法でユーザーと通信し、各 Callback を処理します。
MyCallbackHandler は、ユーザー名の入力を求める NameCallback、パスワードの入力を求める PasswordCallback、SampleLoginModule からユーザーに送信するメッセージ (エラー、警告など) を報告する TextOutputCallback を処理します。
handle
メソッドは、報告するメッセージを抽出し、System.out
に出力することにより、TextOutputCallback を処理します。オプションとして、出力の前に用語説明 (メッセージの型によって異なる) を付けることもできます。報告されるメッセージは、TextOutputCallback の getMessage
メソッドを呼び出すことによって決定されます。メッセージの型は、getMessageType
メソッドを呼び出すことによって決定されます。次は、TextOutputCallback を処理するコードです。
if (callbacks[i] instanceof TextOutputCallback) { // display the message according to the specified type TextOutputCallback toc = (TextOutputCallback)callbacks[i]; switch (toc.getMessageType()) { case TextOutputCallback.INFORMATION: System.out.println(toc.getMessage()); break; case TextOutputCallback.ERROR: System.out.println("ERROR: " + toc.getMessage()); break; case TextOutputCallback.WARNING: System.out.println("WARNING: " + toc.getMessage()); break; default: throw new IOException("Unsupported message type: " + toc.getMessageType()); }
handle
メソッドは、ユーザーにユーザー名の入力を求めることによって NameCallback を処理します。まず、System.err
にプロンプトを出力します。次に、NameCallback の setName
メソッドを呼び出し、ユーザーによって入力された名前を渡すことにより、SampleLoginModule によって使用される名前を設定します。
} else if (callbacks[i] instanceof NameCallback) { // prompt the user for a username NameCallback nc = (NameCallback)callbacks[i]; System.err.print(nc.getPrompt()); System.err.flush(); nc.setName((new BufferedReader (new InputStreamReader(System.in))).readLine());
handle
メソッドが PasswordCallback を処理するときも、System.err
にパスワードの入力を求めるプロンプトを出力します。次に、PasswordCallback の setPassword
メソッドを呼び出し、ユーザーによって入力されたパスワード (次の readPassword
メソッドを呼び出して読み取る) を渡すことにより、SampleLoginModule に使用されるパスワードを設定します。
} else if (callbacks[i] instanceof PasswordCallback) { // prompt the user for sensitive information PasswordCallback pc = (PasswordCallback)callbacks[i]; System.err.print(pc.getPrompt()); System.err.flush(); pc.setPassword(readPassword(System.in));
readPassword
メソッドは、InputStream からパスワードを読み取る基本的なメソッドです。
private char[] readPassword(InputStream in) throws IOException { char[] lineBuffer; char[] buf; int i; buf = lineBuffer = new char[128]; int room = buf.length; int offset = 0; int c; loop: while (true) { switch (c = in.read()) { case -1: case '\n': break loop; case '\r': int c2 = in.read(); if ((c2 != '\n') && (c2 != -1)) { if (!(in instanceof PushbackInputStream)) { in = new PushbackInputStream(in); } ((PushbackInputStream)in).unread(c2); } else break loop; default: if (--room < 0) { buf = new char[offset + 128]; room = buf.length - offset - 1; System.arraycopy(lineBuffer, 0, buf, 0, offset); Arrays.fill(lineBuffer, ' '); lineBuffer = buf; } buf[offset++] = (char) c; break; } } if (offset == 0) { return null; } char[] ret = new char[offset]; System.arraycopy(buf, 0, ret, 0, offset); Arrays.fill(buf, ' '); return ret; } }
SampleLoginModule.java は LoginModule
インタフェースを実装します。SampleLoginModule はチュートリアルのログイン構成ファイルにより、基盤となる適切な認証を実装するクラスとして指定されます。SampleLoginModule のユーザー認証は、ユーザーによって指定された名前とパスワードが特定の値を持っていることを単に検証する処理です。この SampleLoginModule は、チュートリアルのログイン構成ファイルにより、LoginModule として指定されます。これは、(1) あらゆる環境に適切な基本的な認証を実行し、すべてのユーザーのチュートリアルで使用できる (2) 認証技術を実装するログインモジュールを記述する必要がある上級プログラマ向けのログインモジュールの実装例を提供できるからです。
SamplePrincipal.java は、java.security.Principal インタフェースを実装するサンプルクラスです。認証に成功した場合、SampleLoginModule は、ユーザーを表す SamplePrincipal をサブジェクトに追加します。
重要: アプリケーションの作成者は、ログインモジュールやプリンシパル実装の記述方法を理解していなくてもかまいません。SampleLoginModule や SamplePrincipal のコードを確認する必要はありません。アプリケーションを作成し、構成情報 (ログイン構成ファイルの情報など) を指定し、アプリケーションが構成によって指定されたログインモジュールを利用してユーザーを認証できるようにするだけで十分です。使用するログインモジュールを決定し、ログインモジュールのドキュメントからログインモジュールの動作を制御するために (構成内に) 指定できる値を確認してください。
どのベンダーの提供するログインモジュール実装でも使用可能です。「JAAS ログイン構成ファイル」ドキュメントに記載されているように、いくつかの実装が Sun Microsystems の JRE に同梱されています。
LoginModule を記述する必要があるプログラマ向けの情報は、「JAAS LoginModule
開発者ガイド」で確認できます。
JAAS 認証はプラグイン可能な形式で実行されるため、アプリケーションは、基盤となる認証技術から独立した状態を維持できます。システム管理者は、各アプリケーションで使用する認証技術 (ログインモジュール) を決定し、ログイン構成内に構成します。構成情報のソース (ファイルやデータベース) は、現在の javax.security.auth.login.Configuration 実装によって異なります。Sun Microsystems のデフォルトの Configuration
実装は、com.sun.security.auth.login.ConfigFile.html で説明するように、構成ファイルから構成情報を読み取ります。
ログイン構成ファイルとその内容、および使用するログイン構成ファイルの指定方法については、「JAAS ログイン構成ファイル」を参照してください。
すでに説明したように、このチュートリアルで使用するログイン構成ファイル sample_jaas.config には、次のエントリのみが含まれます。
Sample { sample.module.SampleLoginModule required debug=true; };
このエントリの名前は「Sample」です。チュートリアルアプリケーション SampleAcn
がエントリを参照するときに、この名前を使用します。このエントリは、ユーザー認証に使用するログインモジュールが sample.module
パッケージ内の SampleLoginModule であること、および認証が成功したと見なされるためにはこの SampleLoginModule が「成功する」必要があることを示します。SampleLoginModule は、ユーザーから提供された名前とパスワードが正しい (それぞれ testUser と testPassword である) 場合にかぎり成功します。
SampleLoginModule は「debug」オプションも定義します (true
に設定可能)。このオプションの値を true
に設定すると、SampleLoginModule により、認証の進捗に関する追加情報が出力されます。LoginModule で定義できるオプションの数に制限はありません。LoginModule のドキュメントには、構成ファイル内に設定可能なオプションの名前と値が記載されています。
JAAS 認証チュートリアルコードを実行するには、次の操作を行う必要があります。
SampleAcn.java
内の SampleAcn および MyCallbackHandler クラスは sample
パッケージ内にある)。
sample.module
パッケージ内にある)。
sample.principal
パッケージ内にある)。
SampleAcn.java
、SampleLoginModule.java
、SamplePrincipal.java
をコンパイルします。
javac sample/SampleAcn.java sample/module/SampleLoginModule.java sample/principal/SamplePrincipal.javaコマンド全体を 1 行に入力してください。
SampleAcn
アプリケーションを実行します
-Djava.security.auth.login.config==sample_jaas.config
。使用するログイン構成ファイルとして sample_jaas.config
を指定します。次に、完全なコマンドを示します。
java -Djava.security.auth.login.config==sample_jaas.config sample.SampleAcn
ユーザー名とパスワードの入力を求めるプロンプトが表示されます。入力後、ログイン構成ファイルに指定された SampleLoginModule により、その内容の確認が行われます。SampleLoginModule は、ユーザー名「testUser」とパスワード「testPassword」を適切な値として認識します。
debug
オプション (ログイン構成ファイル内で true
に設定) の結果として、SampleLoginModule によりメッセージが出力されます。ログインに成功すると、SampleAcn により、次のメッセージが表示されます。
Authentication succeeded!ログインに失敗した場合 (パスワードのスペルミスなどにより)、次のメッセージが表示されます。
Authentication failed:このあとに、失敗の原因が示されます。たとえば、パスワードのスペルミスがあった場合、次のようなメッセージが表示されます。
Authentication failed: Password Incorrect
SampleAcn のログインの最大試行回数は 3 回です。
セキュリティーマネージャーがインストールされた環境で Java プログラムを実行する場合、有効なセキュリティーポリシーによりアクセス権が明示的に付与されているのでないかぎり、リソースへのアクセスやセキュリティー関連操作の実行は許可されません。J2SE v 1.2 以降と互換性のある Java プラットフォームでは、アクセス権の付与をポリシーファイル内のエントリに指定する必要があります。
大半のブラウザはセキュリティーマネージャーをインストールします。このため、一般にアプレットは、セキュリティーマネージャーに監視されながら実行されます。一方、アプリケーションではそのようなことはありません。これは、アプリケーションの実行時には、セキュリティーマネージャーは自動的にインストールされないためです。このため、SampleAcn
などのアプリケーションでは、デフォルトでリソースへのフルアクセスが可能です。
セキュリティーマネージャーを使用してアプリケーションを実行するには、コマンド行に -Djava.security.manager
引数を含めてインタプリタを呼び出すだけです。
ポリシーファイルは指定せずに、セキュリティーマネージャーを使用して SampleAcn
を呼び出そうすると (必要なアクセス権または AllPermission
を付与するデフォルトポリシー設定をほかの場所で保持しないかぎり)、次のメッセージが表示されます。
% java -Djava.security.manager \ -Djava.security.auth.login.config==sample_jaas.config sample.SampleAcn Exception in thread "main" java.security.AccessControlException: access denied ( javax.security.auth.AuthPermission createLoginContext.Sample)
LoginContext の作成に必要なアクセス権をコードに付与するポリシーファイルが作成および使用されていないため、上に示すように AccessControlException が表示されます。
セキュリティーマネージャーをインストールした環境で SampleAcn
アプリケーションを実行するために必要なすべてのステップを、次に示します。「コードの実行」で説明した手順を実行済みの場合は、最初の 5 つのステップを省略できます。
SampleAcn.java
内の SampleAcn および MyCallbackHandler クラスは sample
パッケージ内にある)。
sample.module
パッケージ内にある)。
sample.principal
パッケージ内にある)。
SampleAcn.java
、SampleLoginModule.java
、SamplePrincipal.java
をコンパイルします。
javac sample/SampleAcn.java sample/module/SampleLoginModule.java sample/principal/SamplePrincipal.javaコマンド全体を 1 行に入力してください。
SampleAcn.class
と MyCallbackHandler.class
を含む JAR ファイルを作成します。
jar -cvf SampleAcn.jar sample/SampleAcn.class sample/MyCallbackHandler.class
コマンド全体を 1 行に入力してください。このコマンドにより、JAR ファイル SampleAcn.jar
が作成され、その内部に SampleAcn.class
と MyCallbackHandler.class
が格納されます。
SampleLoginModule.class
と SamplePrincipal.class
を含む JAR ファイルを作成します。
jar -cvf SampleLM.jar sample/module/SampleLoginModule.class sample/principal/SamplePrincipal.class
LoginContext のインスタンス化を実行するコードに必要なアクセス権は、「createLoginContext.<entry name>」をターゲットとする javax.security.auth.AuthPermission
です。ここで、<entry name> は、アプリケーションが LoginContext のインスタンス化で参照する、ログイン構成ファイルのエントリ名です。コード内に示されているように、SampleAcn
アプリケーションが LoginContext のインスタンス化で使用する名前は、「Sample」です。
LoginContext lc = new LoginContext("Sample", new MyCallbackHandler());
このため、次のアクセス権を SampleAcn.jar
に付与する必要があります。
permission javax.security.auth.AuthPermission "createLoginContext.Sample";
SampleLM.jar
ファイルにもアクセス権を付与する必要があります。どのようなアクセス権を付与する必要があるかについては、ログインモジュールのドキュメントを参照してください。SampleLoginModule の場合、サブジェクトにプリンシパルを追加するには、「modifyPrincipals」をターゲットとする javax.security.auth.AuthPermission
が必要です。
permission javax.security.auth.AuthPermission "modifyPrincipals";
ポリシーファイル sampleacn.policy を、SampleAcn.java
などの格納先ディレクトリにコピーします。これは、次の grant
文を含むポリシーファイルで、SampleAcn.jar
(現在のディレクトリ内) に必要なアクセス権を付与します。
grant codebase "file:./SampleAcn.jar" { permission javax.security.auth.AuthPermission "createLoginContext.Sample"; };
ポリシーファイルには、SampleLM.jar
(カレントディレクトリにもある) に適切なアクセス権を付与する次のような grant
文が含まれています。
grant codebase "file:./SampleLM.jar" { permission javax.security.auth.AuthPermission "modifyPrincipals"; };
注:ポリシーファイルおよびその内部のエントリ構造については、「デフォルトの Policy の実装とポリシーファイルの構文」を参照してください。アクセス権の詳細は、ここを参照してください。
SampleAcn
アプリケーションを実行します
-classpath
節 (SampleAcn.jar
および SampleLM.jar
JAR ファイル内のクラスを検索するため)。-Djava.security.manager
。セキュリティーマネージャーのインストールを指定します。-Djava.security.policy==sampleacn.policy
。使用するポリシーファイルとして sampleacn.policy
を指定します。-Djava.security.auth.login.config==sample_jaas.config
。使用するログイン構成ファイルとして sample_jaas.config
を指定します。次に、Microsoft Windows および Unix システムの両方で使用可能なすべてのコマンドを示します。classpath 項目の区切りとして、UNIX システムではコロンを使用するのに対し、Microsoft Windows システムではセミコロンを使用する点だけが異なります。
次に Microsoft Windows システムの全コマンドを示します。
java -classpath SampleAcn.jar;SampleLM.jar -Djava.security.manager -Djava.security.policy==sampleacn.policy \ -Djava.security.auth.login.config==sample_jaas.config \ sample.SampleAcn
次に UNIX システムの全コマンドを示します。
java -classpath SampleAcn.jar:SampleLM.jar -Djava.security.manager -Djava.security.policy==sampleacn.policy \ -Djava.security.auth.login.config==sample_jaas.config \ sample.SampleAcn
コマンド全体は、1 行で入力してください。ここでは、読みやすくするために複数行に分けて表示してあります。コマンドが長すぎる場合は、.bat ファイル (Microsoft Windows) または .sh ファイル (UNIX) に記述します。このファイルを実行することで、コマンドを実行できます。
指定されたポリシーファイルには、コードに適切なアクセス権を付与するエントリが含まれています。このため、実行は、適切なアクセス権が付与されていないことを示す例外をスローすることなく続行されます。要求に応じてユーザー名 (「testUser」) とパスワード (「testPassword」) を入力すると、ログイン構成ファイルに指定された SampleLoginModule により、入力された名前とパスワードのチェックが行われます。ログインに成功すると「Authentication succeeded!」というメッセージが、失敗すると「Authentication failed.」というメッセージが表示されます。このあとに、失敗の原因が示されます。