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:」の後に失敗の理由が続くメッセージが表示されます。