Java認証・承認サービス(JAAS)は、Java SE 1.3にオプション・パッケージとして導入されました。 JAASはJDK 1.4からJDKに統合されました。
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は、javax.security.auth.callback.CallbackHandlerを呼び出してユーザーとの通信を実行し、ユーザー名やパスワードなどの要求された情報を取得します。
使用する特定のCallbackHandlerのインスタンスを、LoginContextコンストラクタの2番目の引数として指定します。 LoginContextは、このインスタンスを基盤となるログイン・モジュール(ここではSampleLoginModule)に転送します。 通常、アプリケーションは、固有のCallbackHandler実装を提供します。 単純なCallbackHandlerであるTextCallbackHandlerは、com.sun.security.auth.callbackパッケージに提供されており、コマンド行に情報を出力し、入力を読み取ります。 ただし、実際に紹介するのは、固有の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プログラムを実行する場合、有効なセキュリティ・ポリシーによりアクセス権が明示的に付与されているのでないかぎり、リソースへのアクセスやセキュリティ関連操作の実行は許可されません。 アクセス権をポリシー・ファイル内のエントリによって付与する必要があります。
大半のブラウザはセキュリティ・マネージャをインストールするため、一般にアプレットは、セキュリティ・マネージャの監視下で実行されます。 一方、アプリケーションでは、アプリケーションの実行時にセキュリティ・マネージャは自動的にインストールされないため、そのようなことはありません。 このため、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およびSolaris、LinuxおよびMac OS Xシステムで使用するすべてのコマンドを示します。 classpath項目の区切りとして、Solaris、LinuxおよびMac OS Xシステムではコロンを使用するのに対し、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
次に、Solaris、LinuxおよびMac OS Xシステムのすべてのコマンドを示します。
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ファイル(Solaris、LinuxおよびMac OS Xの場合)に記述し、そのファイルを実行して、コマンドを実行する必要がある場合があります。
指定されたポリシー・ファイルには、コードに必要なアクセス権を付与するエントリが含まれているため、実行は、適切なアクセス権が付与されていないことを示す例外なく続行されるはずです。 ユーザー名とパスワードの入力が求められ(「testUser」と「testPassword」を使用)、ログイン構成ファイルに指定されたSampleLoginModuleにより、名前とパスワードがチェックされます。 ログインが成功すると「Authentication succeeded!」というメッセージが表示され、失敗すると「Authentication failed:」の後に失敗の理由が続くメッセージが表示されます。