Java認証・承認サービス(JAAS)リファレンス・ガイド
JAASは、次の2つの目的で使用できます。
- ユーザーを認証する際、Javaコードがアプリケーション、アプレット、Bean、またはサーブレットであるかに関係なく、Javaコードを現在実行しているユーザーを確実かつセキュアに判定する。
 - ユーザーを承認する際、アクションの実行に必要なアクセス制御権(アクセス権)をユーザーが保持していることを確認する。
 
JAASは、Javaバージョンの標準Pluggable Authentication Module (PAM)フレームワークを実装します。
これまで、Javaは、コード・ソース・ベースのアクセス制御(コードの出所および署名者に基づくアクセス制御)を提供してきました。しかし、コードの実行者に基づくアクセス制御を追加実行する機能は不足していました。JAASは、Javaセキュリティ・アーキテクチャを拡張するフレームワークにより、この機能をサポートします。
JAAS認証は、プラガブルな方式で実行されます。これにより、アプリケーションは、基盤となる認証技術から独立した状態になります。アプリケーション自体に変更を加える必要なく、アプリケーションに新規または更新された認証技術をプラグインできます。アプリケーションは、LoginContextオブジェクトをインスタンス化することにより、認証プロセスを有効にし、LoginContextオブジェクトはConfigurationを参照して、認証の実行で使用する認証テクノロジまたはLoginModuleを判別します。一般的なLoginModuleは、ユーザー名およびパスワードの入力を求め、検証します。音声や指紋標本を読み取り、検証できるものもあります。
               
コードを実行するユーザーまたはサービスが認証されると、JAAS承認コンポーネントはコアJava SEアクセス制御モデルと連動して機能し、慎重な操作の必要なリソースへのアクセスを保護します。アクセス制御の決定は、実行コードのCodeSource、およびコードを実行しているユーザーまたはサービス(Subjectオブジェクトで表される)の両方に基づいています。認証に成功した場合、LoginModuleは、関連するPrincipalおよびクレデンシャルを使用してSubjectを更新します。
               
このドキュメントの対象読者
このドキュメントは、CodeSourceベースおよびSubjectベースのセキュリティ・モデルの制約を受けるアプリケーションを設計する必要がある上級開発者を対象としています。LoginModule開発者(認証技術を実装する開発者)は、Java Authentication and Authorization Service (JAAS): LoginModule開発者ガイドを読む前にこれをお読みください。
                  
最初にJAAS認証チュートリアルとJAAS承認チュートリアルでJAASの使用方法の概要と有効なサンプル・コードを確認した上で、このドキュメントから詳細情報を得ることもできます。
関連ドキュメント
このドキュメントでは、読者がすでに次のドキュメントを読んでいることを前提としています。
「JAASログイン・モジュール開発者ガイド」は、認証技術を実装するLoginModuleを記述する必要がある上級プログラマ向けのドキュメントであり、このドキュメントの補足として役立ちます。
                  
次のチュートリアルは、JAAS認証および承認を利用するすべてのユーザーを対象としています。
次のチュートリアルは、JAAS認証および承認のチュートリアルと似ていますが、Kerberos LoginModuleの使用方法の解説が含まれるため、使用する前にKerberosをインストールする必要があります。
この2つのチュートリアルは、認証とセキュアな通信のための基盤技術としてKerberosを利用するJAASおよびJava GSS-APIチュートリアルの紹介に含まれています。
コア・クラスとインタフェース
JAAS関連コア・クラスおよびインタフェースは、共通クラス、認証クラス、および承認クラスの3つのカテゴリに分類できます。
トピック
共通クラス
共通クラスは、JAAS認証および承認コンポーネントの両方に共通です。
JAASの主要クラスは、javax.security.auth.Subjectであり、これは、単一エンティティ(個人など)の関連情報のグループ化を表します。これには、エンティティのPrincipal、publicクレデンシャルおよびprivateクレデンシャルなどがあります。
                     
プリンシパルの表現には、java.security.Principalインタフェースが使用されます。また、JAASにより定義されるクレデンシャルには、任意のオブジェクトを指定できます。
                     
サブジェクト
リソースへのアクセスを承認する場合、最初に、アプリケーションが要求元を認証する必要があります。JAASフレームワークでは、要求元をサブジェクトという用語で表します。サブジェクトは、個人やサービスなどの任意のエンティティです。サブジェクトが認証されると、javax.security.auth.Subjectには関連する識別情報またはプリンシパルが割り当てられます。Subjectには、多数のPrincipalを含めることができます。たとえば、ある個人は、名前Principal (「John Doe」)およびSSN Principal (「123-45-6789」)などを持ち、これらにより、他のサブジェクトと区別されます。
                        
Subjectは、クレデンシャルと呼ばれるセキュリティ関連の属性も保持できます(クレデンシャルの項を参照)。非公開暗号化キーなどの特別な保護が必要な機密の資格は、非公開資格Set内に格納されます。公開キー証明書などの共有されるクレデンシャルは、公開クレデンシャルSet内に格納されます。資格証明Setが異なると、それにアクセスおよび変更するためのアクセス権も異なります。
                        
サブジェクトは、次のコンストラクタを使用して作成されます。
public Subject();
public Subject(boolean readOnly, Set principals,
               Set pubCredentials, Set privCredentials);最初のコンストラクタは、Principalおよびクレデンシャルの空(nullではない)のSetでSubjectを作成します。2番目のコンストラクタは、指定されたPrincipalおよびクレデンシャルのSetでSubjectを作成します。Subjectを読取り専用にするために使用できるboolean型の引数も持っています。読取り専用のSubject内では、PrincipalおよびクレデンシャルSetは不変です。 
                        
アプリケーション作成者がSubjectをインスタンス化する必要はありません。アプリケーションがLoginContextをインスタンス化し、SubjectをLoginContextコンストラクタに渡さない場合、LoginContextは新しい空のSubjectをインスタンス化します。LoginContextの項を参照してください。
                        
Subjectが読取り専用の状態でインスタンス化されなかった場合、次のメソッドを呼び出して読取り専用に設定できます。
                        
public void setReadOnly();ターゲット名「setReadOnly」を持つjavax.security.auth.AuthPermissionは、このメソッドを呼び出すために要求されます。読取り専用の状態に設定したあとで、Principalやクレデンシャルを追加または削除しようとすると、IllegalStateExceptionがスローされます。次のメソッドを使用して、Subjectの読取り専用状態をテストできます。
                        
public boolean isReadOnly();Subjectに関連したPrincipalを取得する場合、次の2つのメソッドを利用できます。
                        
public Set getPrincipals();
public Set getPrincipals(Class c);最初のメソッドは、Subjectに含まれるすべてのPrincipalを返します。一方、2番目のメソッドは、指定されたクラスcのインスタンスまたはクラスcのサブクラスのインスタンスになっているPrincipalしか返しません。Subjectに関連付けられているPrincipalがない場合は、空のセットが返されます。
                        
Subjectに関連した公開クレデンシャルを取得する場合は、次のメソッドを利用できます。
                        
public Set getPublicCredentials();
public Set getPublicCredentials(Class c);これらのメソッドの動作はgetPrincipalsメソッドの動作と似ています。ただし、getPrincipalsメソッドでは、公開クレデンシャルを取得することはできません。
                        
Subjectに関連した非公開クレデンシャルにアクセスする場合は、次のメソッドを利用できます。
                        
public Set getPrivateCredentials();
public Set getPrivateCredentials(Class c);これらのメソッドの動作は、getPrincipalsメソッドやgetPublicCredentialsメソッドとよく似ています。
                        
SubjectのPrincipal Set、公開クレデンシャルSet、または非公開クレデンシャルSetを変更または操作する場合、呼出し側はjava.util.Setクラスで定義されたメソッドを使用します。その方法を示すサンプル・コードを次に示します。
                        
Subject subject;
Principal principal;
Object credential;
// ...
// add a Principal and credential to the Subject
subject.getPrincipals().add(principal);
subject.getPublicCredentials().add(credential);ノート:
「modifyPrincipals」、「modifyPublicCredentials」、「modifyPrivateCredentials」のいずれかのターゲット名を持つAuthPermissionは、それぞれのSetを変更するために要求されます。Subjectの内部セットが返すのは、引数なしのgetPrincipals()、getPublicCredentials()、getPrivateCredentials()メソッドから返されるセットだけです。このため、返されたセットを変更すると、内部セットも影響を受けます。Subjectのそれぞれの内部セットは、getPrincipals(Class c)、getPublicCredentials(Class c)、getPrivateCredentials(Class c)メソッドから返されるセットは返しません。メソッド呼び出しごとに、新規セットが作成され、返されます。これらのセットを変更しても、Subjectの内部セットに影響はありません。
                        非公開資格証明のSetで繰返し処理を実行するには、各資格証明にアクセスするため、javax.security.auth.PrivateCredentialPermissionが必要です。詳細は、PrivateCredentialPermission APIドキュメントを参照してください。
                        
次のメソッドは、指定したSubjectとしてアクションを実行できます:
                        
public static <T> T callAs(Subject subject, Callable<T> action)
public static <T> T doAs(Subject subject, PrivilegedAction<T> action)
public static <T> T doAsPrivileged(Subject subject,
                                   PrivilegedAction<T> action,
                                   AccessControlContext acc)ノート:
doAsおよびdoAsPrivilegedメソッドは、削除予定の非推奨です。かわりにcallAsを使用してください。セキュリティ・マネージャが許可されている場合、callAs、doAsまたはdoAsPrivilegedを起動すると、SubjectをAccessControlContextに関連付けることができます。次のメソッドは、指定されたAccessControlContextと関連付けられたSubjectを返すか、指定されたAccessControlContextと関連付けられたSubjectが存在しない場合には、nullを返します。
                        
public static Subject getSubject(final AccessControlContext acc);ターゲット名「getSubject」を持つAuthPermissionは、getSubjectを呼び出すために要求されます。
                        
ノート:
getSubjectメソッドは、削除予定の非推奨になりました。かわりにSubject::currentを使用してください。詳細は、「特定のSubjectとしてアクションを実行するためのdoAsメソッド」を参照してください。
セキュリティ・マネージャが許可されていない場合、doAsまたはcallAsメソッドを使用してアクションを実行すると、Subjectは実行期間にバインドされます。次のメソッドは、現在のスレッドの実行期間に関連付けられたSubjectを返し、Subjectが設定されていない場合はnullを返します:
                        
public static Subject current();セキュリティ・マネージャが許可されていない場合、getSubjectを使用してSubjectを取得しようとすると、UnsupportedOperationExceptionが表示されます。
                        
詳細は、特定のSubjectとしてアクションを実行するためのcallAsおよび現在のメソッドを参照してください。
Subjectクラスには、java.lang.Objectから継承した次のメソッドも含まれます。
                        
public boolean equals(Object o);
public String toString();
public int hashCode();
                        
                     特定のSubjectとしてアクションを実行するためのcallAsおよび現在のメソッド
ノート:
Subject::doAsおよびSubject::getSubjectメソッドは、Subject::callAsおよびSubject:currentメソッドにそれぞれ置き換えられます。これらのメソッドは、削除予定の非推奨になったセキュリティ・マネージャAPIに依存しているため、削除予定の非推奨です。説明および代替方法は、JEP 411: 削除のためのセキュリティ・マネージャの非推奨化を参照してください。次の静的メソッドを呼び出して、特定のSubjectとしてアクションを実行します:
                           
public static <T> T callAs(Subject subject, Callable<T> action)
    throws CompletionExceptionJAAS承認チュートリアルのSampleAzn.javaの例では、次のようにSubject::callAsが呼び出されます:
                           
Subject.callAs(mySubject, anotherAction);この例では、引数anotherActionは、メソッドSubject::currentを使用してSubject mySubjectにアクセスします:
                           
Callable<Void> anotherAction = () -> {
    
    Subject s = Subject.current();
    System.out.println("\nCurrent subject: " + s);
    
    // Add a new Principal
    Random r = new Random();
    String pn = Integer.toString(r.nextInt());
    Principal p = new sample.principal.SamplePrincipal(pn);
    
    System.out.println("\nAdding principal " + pn);
    s.getPrincipals().add(p);         
    
    // List Principals
    System.out.println("\nAuthenticated user has the following Principals:");
    Iterator pi = s.getPrincipals().iterator();
    while (pi.hasNext()) {
        Principal nextp = (Principal)pi.next();
        System.out.println("\t" + nextp.toString());            
    }
    
    System.out.println("\nYour java.home property: "
        + System.getProperty("java.home"));
    System.out.println("\nYour user.home property: "
        + System.getProperty("user.home"));
    File f = new File("foo.txt");
    System.out.print("\nfoo.txt does ");
    if (!f.exists())
        System.out.print("not ");
    System.out.println("exist in the current working directory.");
    
    return null;            
};子スレッドが構造化並行性を使用して親のスレッドの実行内で起動および終了した場合、子スレッドはSubject::currentメソッドを使用して親スレッドの現在のSubjectにアクセスできます。次の例は、SampleAzn.javaからのものです:
                           
Callable<Void> addRandomPrincipal = () -> {
    
    Subject s = Subject.current();
    
    // Add a new Principal
    Random r = new Random();
    String pn = Integer.toString(r.nextInt());
    Principal p = new sample.principal.SamplePrincipal(pn);
    
    System.out.println("\nAdding principal " + pn);
    s.getPrincipals().add(p);         
    return null;
};
Callable<Void> structuredAction = () -> {
    try (var scope = new StructuredTaskScope<>()) {
        scope.fork(addRandomPrincipal);
        scope.fork(addRandomPrincipal);
        scope.fork(addRandomPrincipal);
        scope.join();
                
        Subject s = Subject.current();
        System.out.println("\nCurrent subject: " + s);
    
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return null;
};        
        
Subject.callAs(mySubject, structuredAction);メソッドStructuredTaskScope::forkは、タスク・スコープscopeで新しいスレッドを起動します。このメソッドは、StructuredTaskScope.Subtaskまたは分岐サブタスクを返します。この例では、3つの分岐サブタスクを作成します。各サブタスクは、Subject::currentメソッドを使用して親スレッドの現在のSubjectを取得し、新しいプリンシパルをSubjectに追加します。メソッドStructuredTaskScope::joinは、scopeのサブタスクが終了するまで待機してから続行します。次に、Subjectを出力します。この例では、3つの追加プリンシパルが含まれています。
                           
この例の出力を表示するには、「承認チュートリアル・コードの実行」を参照してください。
ノート:
クラスStructuredTaskScopeは、プレビュー機能である構造化並行性APIの一部です。プレビュー機能は、設計、仕様および実装が完了したが、永続的でない機能です。プレビュー機能は、将来のJava SEリリースで、異なる形式で存在することもあれば、まったく存在しないこともあります。プレビュー機能が含まれているコードをコンパイルして実行するには、--enable-preview -source 23などの追加のコマンドライン・オプションを指定する必要があります。『Java Platform, Standard Edition Java言語更新』の言語およびVM機能のプレビューおよび『Java Platform, Standard Editionコア・ライブラリ』の構造化並行性に関する項を参照してください。
                           特定のSubjectとしてアクションを実行するdoAsメソッド
ノート:
Subject::doAsおよびSubject::getSubjectメソッドは、Subject::callAsおよびSubject:currentメソッドにそれぞれ置き換えられます。これらのメソッドは、削除予定の非推奨になったセキュリティ・マネージャAPIに依存しているため、削除予定の非推奨です。説明および代替方法は、JEP 411: 削除のためのセキュリティ・マネージャの非推奨化を参照してください。また、Subject::doAsメソッドの動作は、セキュリティ・マネージャが許可されているか、許可されていないかによって異なることに注意してください。Subject JavaDoc APIドキュメントの非推奨のメソッドおよび置換を参照してください。
Subject::doAsPrivilegedメソッドも、セキュリティ・マネージャAPIに依存しているため、削除予定の非推奨になりました。ただし、このメソッドの代わりとなるものはありません。
 次のstaticメソッドは、呼び出されると特定のSubjectとしてアクションを実行します。
                           
public static Object
    doAs(final Subject subject,
         final java.security.PrivilegedAction action);
public static Object
    doAs(final Subject subject,
         final java.security.PrivilegedExceptionAction action)
         throws java.security.PrivilegedActionException;セキュリティ・マネージャが許可されている場合は、両方のメソッドで指定されたSubjectを現行スレッドのAccessControlContextに関連付けてから、PrivilegedActionまたはPrivilegedExceptionActionを実行します。これにより、Subjectとしてアクションを実行させる効果を実現します。最初のメソッドでは、ランタイム例外がスローされる可能性がありますが、通常の実行では、そのaction引数のrunメソッドからのObjectが返されます。2番目のメソッドも、PrivilegedExceptionAction runメソッドからチェック例外をスローする場合があることを除き、動作は同じです。doAsをターゲットとするAuthPermissionは、doAsメソッドを呼び出すために要求されます。
                           
セキュリティ・マネージャが許可されていない場合、Subjectは現在のスレッドのAccessControlContextに関連付けられません。かわりに、実行期間にバインドされます。たとえば、セキュリティ・マネージャが許可されていない場合、次の例では、getSubjectを呼び出すとUnsupportedOperationExampleがスローされます。
                           
PrivilegedAction<Object> privAct = () -> {
    AccessControlContext acc = AccessController.getContext();
    // Subject::getSubject throws an UnsupportedOperationExample
    // if a security manager isn't allowed
    Subject s = Subject.getSubject(acc);
    System.out.println("Current subject: " + s);
    return null;
};
Subject.doAs(mySubject, privAct);かわりにSubject::currentを使用できます:
PrivilegedAction<Object> privAct = () -> {
    Subject s = Subject.current();
    System.out.println("Current subject: " + s);
    return null;
};
Subject.doAs(mySubject, privAct);ただし、doAsのかわりにcallAsを使用します。特定のSubjectとしてアクションを実行するためのcallAsおよび現在のメソッドを参照してください。
                           
Subject.doAsの例
次に、doAsメソッドを最初に利用する場合の例を示します。ユーザー「Bob」がLoginContext (LoginContextを参照)によって認証されたため、com.ibm.security.PrincipalクラスのPrincipalでSubjectが追加され、Principalの名前が「BOB」になったと想定してください。また、SecurityManagerが許可されていて、アクセス制御ポリシー内に次が存在するものとします(ポリシー・ファイルの詳細はPolicyを参照)。
                              
    // grant "BOB" permission to read the file "foo.txt"
    grant Principal com.ibm.security.Principal "BOB" {
        permission java.io.FilePermission "foo.txt", "read";
    };次に、サンプル・アプリケーション・コードを示します。
    class ExampleAction implements java.security.PrivilegedAction {
        public Object run() {
            java.io.File f = new java.io.File("foo.txt");
            // the following call invokes a security check
            if (f.exists()) {
                System.out.println("File foo.txt exists");
            }
            return null;
        }
    }
    public class Example1 {
        public static void main(String[] args) {
            // Authenticate the subject, "BOB".
            // This process is described in the
            // LoginContext class.
            Subject bob;
            // Set bob to the Subject created during the
            // authentication process
            // perform "ExampleAction" as "BOB"
            Subject.doAs(bob, new ExampleAction());
        }
    }実行時に、ExampleActionがf.exists()を呼び出すと、セキュリティ・チェックが実行されます。ただし、ExampleActionが「BOB」として実行中であり、必要なFilePermissionがこの例のポリシーによって「BOB」に付与されているため、ExampleActionはセキュリティ・チェックを通過します。ポリシー内のgrant文を変更する(たとえば、不正なCodeBaseを追加するかPrincipalを「MOE」に変更する)と、SecurityExceptionがスローされます。
                              
doAsPrivilegedメソッド
次のメソッドも、特定のSubjectとしてアクションを実行します。
                              
public static Object doAsPrivileged(
    final Subject subject,
    final java.security.PrivilegedAction action,
    final java.security.AccessControlContext acc);
public static Object doAsPrivileged(
    final Subject subject,
    final java.security.PrivilegedExceptionAction action,
    final java.security.AccessControlContext acc)
    throws java.security.PrivilegedActionException;doAsPrivilegedをターゲットとするAuthPermissionは、doAsPrivilegedメソッドを呼び出すために要求されます。
                              
doAsとdoAsPrivileged
doAsPrivilegedメソッドの動作は、doAsとまったく同じですが、指定されたSubjectを現行のスレッドのAccessControlContextに関連付けるかわりに、指定されたAccessControlContextを使用する点が異なります。このように、現行のものとは異なったAccessControlContextによってアクションが制限されることがあります。
                              
AccessControlContextには、AccessControlContextのインスタンス化以降に実行されたすべてのコードに関する情報(コード位置や、ポリシーによってコードに付与されたアクセス権など)が含まれます。アクセス制御チェックを成功させるため、ポリシーは、AccessControlContextによって参照される各コード項目に、必要なアクセス権を付与する必要があります。
                              
doAsPrivilegedに提供されたAccessControlContextがnullである場合、アクションが別のAccessControlContextによって制限されることはありません。このことは、サーバー環境で役立つ場合があります。サーバーは、複数の着信要求を認証し、各要求に対して別々のdoAsを実行できます。各doAsアクションを新たに開始し、現行のサーバーの制限AccessControlContextをなくすため、サーバーはdoAsPrivilegedを呼び出し、null AccessControlContextを渡すことができます。
                              
プリンシパル
前述したように、サブジェクトが認証されると、関連する識別情報またはプリンシパルがサブジェクトに割り当てられます。サブジェクトには、多数のプリンシパルを含めることができます。たとえば、ユーザーは、他のサブジェクトと明確に区別される名前プリンシパル(「John Doe」)およびSSNプリンシパル(「123-45-6789」)を保持できます。プリンシパルはjava.security.Principalおよびjava.io.Serializableインタフェースを実装する必要があります。サブジェクトに関連付けられたプリンシパルの更新方法の詳細は、サブジェクトを参照してください。
クレデンシャル
サブジェクトは、関連付けられたプリンシパルに加え、クレデンシャルと呼ばれる独自のセキュリティ関連属性を保持できます。クレデンシャルには、新規サービスに対してサブジェクトを認証する際に使用可能な情報が含まれます。この種のクレデンシャルには、パスワード、Kerberosチケット、および公開キー証明書が含まれます。クレデンシャルには、サブジェクトによる特定操作の実行を可能にするだけのデータが含まれる場合もあります。たとえば、暗号化キーは、サブジェクトによるデータへの署名または暗号化を可能にするクレデンシャルを表します。公開および非公開クレデンシャル・クラスは、コアJAASクラス・ライブラリの一部ではありません。したがって、あらゆるクラスがクレデンシャルを表すことができます。
 公開および非公開クレデンシャル・クラスは、コアJAASクラス・ライブラリの一部ではありません。ただし、開発者は、クレデンシャルに関連する2つのインタフェース、RefreshableおよびDestroyableを実装するクレデンシャル・クラスを持つことを決定できます。
                        
トピック
Refreshable
このjavax.security.auth.Refreshable インタフェースは、クレデンシャルの自動リフレッシュ機能を提供します。たとえば、有効期間の制限された資格がこのインタフェースを実装すると、呼出し側が有効期間を更新できるようになります。このインタフェースには、次の2つのabstractメソッドがあります。
                           
    boolean isCurrent();
このメソッドは、クレデンシャルが現在有効かどうかを判定します。
    void refresh() throws RefreshFailedException;
このメソッドは、クレデンシャルの有効期間を更新または延長します。このメソッドの実装では、次を実行します
AuthPermission("refreshCredential")
クレデンシャルをリフレッシュするためのアクセス権が呼出し側にあることを確認するためのセキュリティ・チェック。
Destroyable
このjavax.security.auth.Destroyable インタフェースは、クレデンシャル内のコンテンツを破棄する機能を提供します。このインタフェースには、次の2つのabstractメソッドがあります。
                           
    boolean isDestroyed();
このメソッドは、クレデンシャルが破棄されたかどうかを判別します。
    void destroy() throws DestroyFailedException;
このメソッドは、このクレデンシャルに関連した情報を破棄およびクリアします。以降、このクレデンシャルに対して特定のメソッドを呼び出すと、IllegalStateExceptionがスローされます。このメソッドの実装では、AuthPermission("destroyCredential")セキュリティ・チェックを実行し、呼出し側がクレデンシャルを破棄するためのアクセス権を保持することを確認すべきです。
                           
認証クラスとインタフェース
認証は、サブジェクトの識別情報を検証するプロセスを表し、セキュアな方法で実行する必要があります。さもないと、悪意のあるユーザーが他のユーザーを装ってシステムへのアクセスを試みる可能性があります。通常、認証には、識別情報を証明するなんらかの形式の証拠を示すサブジェクトが含まれます。この種の証拠には、サブジェクトのみが知るか保持できる情報(パスワードや指紋など)またはサブジェクトのみが作成可能な情報(秘密キーを使用した署名付きデータなど)があります。
サブジェクト(ユーザーまたはサービス)の認証では、次のステップが行われます。
- アプリケーションが
LoginContextをインスタンス化します。 LoginContextが、Configurationに問い合わせを行い、アプリケーション用に構成されたすべてのLoginModuleをロードします。- アプリケーションが、
LoginContextのloginメソッドを呼び出します。 loginメソッドがロードされたすべてのLoginModuleを呼び出します。各LoginModuleはサブジェクトを認証しようとします。成功した場合、LoginModuleは、認証されるサブジェクトを表すSubjectオブジェクトに、適切なPrincipalとクレデンシャルを関連付けます。LoginContextが、認証ステータスをアプリケーションに返します。- 認証が成功すると、アプリケーションは
SubjectをLoginContextから取得します。 
次の項では、認証クラスについて説明します。
LoginContext
javax.security.auth.login.LoginContextクラスは、サブジェクトの認証に使用する基本的なメソッドを提供し、基盤となる認証テクノロジから独立したアプリケーションを開発する方法を提供します。LoginContextは、Configurationを調べて、特定のアプリケーション用に構成された認証サービスまたはLoginModuleを判別します。このため、アプリケーション自体に変更を加えることなく、アプリケーションに様々なLoginModuleをプラグインできます。
                        
LoginContextは、選択可能な次の4つのコンストラクタを提供します。
                        
    public LoginContext(String name) throws LoginException;
    public LoginContext(String name, Subject subject) throws LoginException;
    public LoginContext(String name, CallbackHandler callbackHandler)
           throws LoginException
    public LoginContext(String name, Subject subject,
           CallbackHandler callbackHandler) throws LoginExceptionすべてのコンストラクタは、共通のパラメータnameを共有します。LoginContextは、この引数をログインConfigurationのインデックスとして使用し、LoginContextのインスタンス化を行うアプリケーション用として構成されるLoginModuleを特定します。Subjectを入力パラメータとして取らないコンストラクタは、新規Subjectをインスタンス化します。どのコンストラクタでも、nullの入力は許可されません。呼出し元は、createLoginContext.<name>をターゲットとするAuthPermissionに対して、LoginContextのインスタンス化を要求します。ここで、<name>は、アプリケーションがLoginContextのインスタンス化の際にnameパラメータで参照するログイン構成エントリの名前です。
                        
CallbackHandlerとこれが必要な状況の詳細は、CallbackHandlerを参照してください。
                        
実際の認証は、次のメソッドへの呼出しを使って行われます。
    public void login() throws LoginException;loginを呼び出すと、すべての構成済みLoginModuleが呼び出され、認証を実行します。認証に成功した場合は、次のメソッドを使用して、認証されたSubject (Principal、公開クレデンシャル、非公開クレデンシャルを保持している場合がある)を取得できます。
                        
     public Subject getSubject();Subjectをログアウトして、認証済みのPrincipalsおよびクレデンシャルを削除するには、次のメソッドを使用します。
                        
    public void logout() throws LoginException;次のサンプル・コードは、サブジェクトの認証およびログアウトに必要な呼出しを示します。
    // let the LoginContext instantiate a new Subject
    LoginContext lc = new LoginContext("entryFoo");
    try {
        // authenticate the Subject
        lc.login();
        System.out.println("authentication successful");
        // get the authenticated Subject
        Subject subject = lc.getSubject();
        ...
        // all finished -- logout
        lc.logout();
    } catch (LoginException le) {
        System.err.println("authentication unsuccessful: " +
            le.getMessage());
    }LoginModule
LoginModule インタフェースを使用すると、異なる種類の認証技術を実装して、アプリケーションでプラグインとして利用できます。たとえば、ユーザー名/パスワードベースの認証を実行できるLoginModuleがあります。スマート・カードやバイオメトリック・デバイスなどのハードウェアへのインタフェースを提供するLoginModuleもあります。
                        
ノート: アプリケーション作成者は、LoginModuleの機能を理解していなくてもかまいません。アプリケーションを作成し、構成情報(ログイン構成ファイルの情報など)を指定し、アプリケーションが構成によって指定されたログイン・モジュールを利用してユーザーを認証できるようにするだけで十分です。
                        
一方、認証技術を実装するLoginModuleを作成する必要があるプログラマは、Java Authentication and Authorization Service (JAAS): LoginModule開発者ガイドで具体的なステップを確認してください。
CallbackHandler
LoginModuleがユーザーと通信して、認証情報を取得する必要がある場合があります。LoginModuleは、この目的でjavax.security.auth.callback.CallbackHandlerを使用します。アプリケーションはCallbackHandlerインタフェースを実装し、それをLoginContextに転送します。LoginContextは、それを基礎となるLoginModuleに直接転送します。LoginModuleはCallbackHandlerを使用して、ユーザーからの入力(パスワードやスマート・カードの暗証番号など)を収集したり、ユーザーに情報(ステータス情報など)を提供したりします。アプリケーションにCallbackHandlerの指定を許可することにより、基礎となるLoginModulesは、アプリケーションとユーザー間の通信方法から独立した状態にすることができます。たとえば、GUIアプリケーション用のCallbackHandlerの実装は、ウィンドウを表示してユーザーの入力を求めることができます。一方、非GUIツール用のCallbackHandlerの実装では、単にユーザーにコマンド行から直接入力するように求めることができます。
                        
CallbackHandler
1つのメソッドを実装するインタフェースです。
     void handle(Callback[] callbacks)
         throws java.io.IOException, UnsupportedCallbackException;
LoginModuleはCallbackHandler handleメソッドに適切なCallback (ユーザー名に対してはNameCallback、パスワードに対してはPasswordCallback)で構成される配列を渡し、CallbackHandlerは要求に従ってユーザーと通信し、Callback内に適切な値を設定します。たとえば、NameCallbackを処理する場合、CallbackHandlerはユーザーから名前を取得し、NameCallbackのsetNameメソッドを呼び出してその名前を格納します。
                        
CallbackHandlerのドキュメントには、このドキュメントには記載されていない大量のサンプルが記載されています。
                        
コールバック
javax.security.auth.callbackパッケージには、コールバック・インタフェースおよびいくつかの実装が含まれています。LoginModulesは、コールバックの配列を、CallbackHandlerのhandleメソッドに直接渡すことができます。
                        
使用方法の詳細については、各種Callback APIを参照してください。
承認クラス
JAAS承認を行うには、実行中のコードと実行ユーザーに基づいてアクセス制御権を付与し、次の作業を行う必要があります。
- 「LoginContext」セクションの説明に従ってユーザーを認証する。
 - 認証の結果生成されるサブジェクトを「サブジェクト」セクションの説明に従ってアクセス制御コンテキストに関連付ける。
 - セキュリティ・ポリシー内にプリンシパルベースのエントリを構成する必要があります。
 
次の各項では、Policy抽象クラスと認可固有のクラスAuthPermissionおよびPrivateCredentialPermissionについて説明します。
                     
Policy
java.security.Policyクラスは、システム全体のアクセス制御ポリシーを表す抽象クラスです。Policy APIはPrincipalベースの問合せをサポートしています。
                        
JDKは、デフォルトで、ファイルベースのサブクラス実装を提供します。この実装もアップグレードされ、ポリシー・ファイル内でPrincipalベースのgrantエントリを使用できるようになっています。
                        
ポリシー・ファイルおよびその内部のエントリ構造については、「デフォルトのPolicyの実装とポリシー・ファイルの構文」を参照してください。
AuthPermission
javax.security.auth.AuthPermissionクラスは、JAASに必須の基本的なアクセス権をカプセル化しています。AuthPermissionには名前(「ターゲット名」とも呼ばれる)は含まれますが、アクション・リストは含まれません。名前付きアクセス権を得るか、得ないかのどちらかになります。
                        
AuthPermissionは、java.security.Permissionから継承されたメソッドのほかに2つのpublicコンストラクタを持っています。
                        
    public AuthPermission(String name);
    public AuthPermission(String name, String actions);
最初のコンストラクタは、指定された名前で新しいAuthPermissionを作成します。2番目のコンストラクタも、指定された名前で新しいAuthPermissionオブジェクトを作成しますが、追加のactions引数があります。これは現在のところ未使用であるため、nullにしてください。このコンストラクタは、Policyオブジェクトで新しいPermissionオブジェクトをインスタンス化するためだけに存在します。その他のほとんどのコードでは、最初のコンストラクタが適しています。
                        
現在のところ、AuthPermissionオブジェクトを使用して、Policy、Subject、LoginContext、およびConfigurationオブジェクトへのアクセスを保護します。サポートされる有効な名前のリストについては、AuthPermissionのJavaDoc APIドキュメントを参照してください。
                        
PrivateCredentialPermission
javax.security.auth.PrivateCredentialPermissionクラスは、Subjectの非公開クレデンシャルへのアクセスを保護し、1つのpublicコンストラクタを提供します。
                        
     public PrivateCredentialPermission(String name, String actions);
JAASチュートリアルとサンプル・プログラム
「JAAS認証」および「JAAS承認」の各チュートリアルには、次のサンプルが含まれています。
SampleAcn.javaは、JAAS認証を説明するサンプル・アプリケーションです。SampleAzn.javaは、承認チュートリアルで使用されるサンプル・アプリケーションです。認証と承認の両方を説明します。- JAAS認証チュートリアルのログイン構成ファイルでは、
sample_jaas.config(両方のチュートリアルで使用されるサンプル・ログイン構成ファイル)について説明します。 sampleacn.policyは、認証チュートリアルのコードに必要なアクセス権を付与するサンプル・ポリシー・ファイルです。sampleazn.policyは、承認チュートリアルのコードに必要なアクセス権を付与するサンプル・ポリシー・ファイルです。SampleLoginModule.javaは、チュートリアルのログイン構成ファイル(sample_jaas.config)によって、基盤となる適切な認証を実装するクラスとして指定されるクラスです。SampleLoginModuleのユーザー認証は、ユーザーによって指定された名前とパスワードが特定の値を持っていることを単に検証する処理です。SamplePrincipal.javaは、Principalインタフェースを実装するサンプル・クラスです。SampleLoginModuleによって使用されます。
アプリケーション、ポリシー・ファイル、およびログイン構成ファイルの詳細については、チュートリアルを参照してください。
チュートリアルに説明されているとおり、アプリケーション作成者はSampleLoginModule.javaやSamplePrincipal.javaのコードを理解していなくてもかまいません。LoginModulesを作成するプログラマは、Java Authentication and Authorization Service (JAAS): LoginModule開発者ガイドでその方法を学習できます。
                  
付録A: java.securityセキュリティ・プロパティ・ファイルでのJAAS設定
java.securityマスター・セキュリティ・プロパティ・ファイル内で多数のJAAS関連設定を構成できます。このファイルは、JDKのconf/securityディレクトリ内にあります。
                  
JAASは、java.securityに次の2つの新しいセキュリティ・プロパティを追加します。
                  
login.configuration.providerlogin.config.url.n
次の既存のプロパティもJAASユーザーと関係があります。
policy.providerpolicy.url.n
次に、これらのプロパティを構成する方法の例を示します。この例では、policy.provider、policy.url.nおよびlogin.configuration.providerセキュリティ・プロパティのデフォルトのjava.securityファイル内の値はそのまま使用します。デフォルトのjava.securityファイルでは、login.config.url.nセキュリティ・プロパティの値も一覧表示されていますがコメント化されています。次の例ではコメント化されていません。
                  
...
#
# Class to instantiate as the javax.security.auth.login.Configuration
# provider.
#
login.configuration.provider=sun.security.provider.ConfigFile
#
# Default login configuration file
#
#login.config.url.1=file:${user.home}/.java.login.config
#
# Class to instantiate as the system Policy. This is the name of the class
# that will be used as the Policy object. The system class loader is used to
# locate this class.
#
policy.provider=sun.security.provider.PolicyFile
# The default is to have a single system-wide policy file,
# and a policy file in the user's home directory.
#
policy.url.1=file:${java.home}/conf/security/java.policy
policy.url.2=file:${user.home}/.java.policy
...
ノート:
このファイルへの変更は、後続のJDK更新によって上書きされることがあります。ただし、システム・プロパティjava.security.properties=<URL>を使用して、代替のjava.securityプロパティ・ファイルをコマンド行から指定できます。このプロパティ・ファイルはシステム・プロパティ・ファイルの末尾に追加されます。両方のプロパティ・ファイルが同じキーに対する値を指定している場合、コマンド行プロパティ・ファイルが最後にロードされるため、このファイルの値が選択されます。
                     
また、java.security.properties==<URL>と指定すると(2つの等号を使用)、プロパティ・ファイルはシステム・プロパティ・ファイルを完全にオーバーライドします。
                     
追加のプロパティ・ファイルをコマンド行から指定するための機能を無効にするには、システム・プロパティ・ファイル内のキーsecurity.overridePropertiesFileをfalseに設定します。これはデフォルトではtrueに設定されています。
                     
ログイン構成プロバイダ
Oracleが提供するデフォルトのJAASログイン構成実装は、その構成情報をファイルから取得します。この情報は、チュートリアルに記載されている特殊な形式で提供されることになっています。
代替プロバイダ・クラス実装をlogin.configuration.providerプロパティ内に指定することで、デフォルトのJAASログイン構成実装を置き換えることができます。
                     
たとえば:
    login.configuration.provider=com.foo.Config
セキュリティ・プロパティlogin.configuration.providerが見つからない、または指定されていない場合、デフォルト値が設定されます。 
                     
    login.configuration.provider=com.sun.security.auth.login.ConfigFile
ログイン構成プロバイダを、コマンド行から動的に設定することはできません。
ログイン構成URL
Oracleが提供するデフォルト実装のように、ファイル内に構成情報が指定されていることを期待するログイン構成実装を使用している場合は、login.config.url.nプロパティに個々のURLを指定することにより、ログイン構成ファイルの位置を静的に設定できます。nは、1から始まる連続した番号の整数です。複数の構成ファイルが指定されている場合(n >= 2の場合)、それらは読み込まれ、結合されて単一の構成になります。
                     
たとえば:
  login.config.url.1=file:C:/config/.java.login.config
  login.config.url.2=file:C:/users/foo/.foo.login.config
構成ファイルの位置がjava.securityプロパティ・ファイルに指定されておらず、コマンド行から-Djava.security.auth.login.configオプションを使って動的に指定されてもいない場合、JAASは次からデフォルト構成のロードを試みます。
                     
file:${user.home}/.java.login.config
ポリシー・プロバイダ
代替プロバイダ・クラス実装をpolicy.providerプロパティ内に指定することで、デフォルトのポリシー実装を置き換えることができます。
                     
たとえば:
policy.provider=com.foo.Policy
セキュリティ・プロパティpolicy.providerが見つからない、または指定されていない場合、Policyはデフォルト値に設定されます。
                     
policy.provider=sun.security.provider.PolicyFile
ポリシー・プロバイダを、コマンド行から動的に設定することはできません。
ポリシー・ファイルURL
アクセス制御ポリシー・ファイルの位置は、auth.policy.url.nプロパティにそれぞれのURLを指定することにより、静的に設定できます。nは、1から始まる連続した番号の整数です。複数のポリシーが指定されている場合(n >= 2の場合)、それらは読み込まれ、結合されて単一のポリシーになります。
                     
たとえば:
policy.url.1=file:C:/policy/.java.policy
policy.url.2=file:C:/users/foo/.foo.policy
java.securityプロパティ・ファイルにポリシー・ファイルの位置が指定されておらず、-Djava.security.policyオプションによってコマンド行から動的に指定されることもない場合、アクセス制御ポリシーは、JDKと同時にインストールされたシステム・ポリシー・ファイルのポリシーとデフォルトで同じになります。このポリシー・ファイルの特徴は次のとおりです
                     
- 標準の拡張機能にすべてのアクセス権を付与
 - 任意のユーザーが非特権ポートで待機することを許可
 - 任意のコードがセキュリティ上それほど重要でない特定の「標準」プロパティ(
os.name、file.separatorプロパティなど)を読み取ることを許可。 
付録B: JAASログイン構成ファイル
JAAS認証はプラガブルな形式で実行されるため、Javaアプリケーションは、基盤となる認証技術から独立した状態を維持できます。適切な認証技術などの構成情報は実行時に指定されます。構成情報のソース(ファイルやデータベース)は、現在のjavax.security.auth.login.Configuration実装によって異なります。デフォルトのConfiguration実装ConfigFileは、その構成情報をログイン構成ファイルから取得します。JAASの提供するデフォルト・ログインConfiguration実装の詳細は、com.sun.security.auth.login.ConfigFileクラスを参照してください。
                  
ログイン構成ファイルの構造および内容
ログイン構成ファイルは、1つ以上のエントリで構成され、各エントリには、特定のアプリケーションで使用される基礎となる認証技術が指定されます。各エントリの構造を、次に示します。
<name used by application to refer to this entry> { 
    <LoginModule> <flag> <LoginModule options>;
    <optional additional LoginModules, flags and options>;
};
このため、各ログイン構成ファイルのエントリは、名前、続いて1つ以上のLoginModule固有エントリから構成され、LoginModule固有の各エントリはセミコロンで終わり、LoginModule固有エントリ・グループ全体が中カッコで囲まれます。各構成ファイルのエントリはセミコロンで終わります。
例6-1 JAAS認証チュートリアルのログイン構成ファイル
たとえば、JAAS認証チュートリアルのチュートリアルで使用するログイン構成ファイルには、次のエントリのみが含まれます
Sample {
   sample.module.SampleLoginModule required debug=true;
};
ここでは、エントリの名前はSampleで、JAAS認証チュートリアル・アプリケーション(SampleAcn.java)がこのエントリを参照するために使用する名前です。このエントリは、ユーザー認証の実行に使用するLoginModuleがsample.moduleパッケージ内のSampleLoginModuleであること、および認証が成功したと見なされるためにはこのSampleLoginModuleが「成功する」必要があることを示します。SampleLoginModuleは、ユーザーから提供された名前とパスワードが期待したもの(それぞれtestUserとtestPassword)である場合にかぎり成功します。
                        
JAAS認証チュートリアルのJAAS認証チュートリアルで説明したように、ログイン構成ファイルのエントリの名前は、LoginContextのインスタンス化時にアプリケーションがエントリの参照に使用する名前です。アプリケーション開発者は、任意の名前を設定できます。ここで、「アプリケーション」とは、JAASログインを実行するあらゆるコードを指します。
指定したLoginModulesは、認証プロセスの制御に使用されます。認証は、Configurationクラスで説明するように、リスト内を指定された正確な順番で下に進みます。
各LoginModule固有のエントリ・サブパートを、次に示します。
- 
                              
LoginModule: これは、目的の認証技術を実装するクラスを指定します。特に、このクラスは、
javax.security.auth.spiパッケージに含まれるLoginModuleクラスのサブクラスでなければなりません。通常、LoginModuleは、これらのチュートリアルで使用するSampleLoginModule(sample.moduleパッケージ内)と同じように、ユーザー名とパスワードを要求し、検証します。どのベンダーの提供するログイン・モジュール実装でも使用可能です。いくつかの実装が、OracleのJDKに同梱されています。様々なLoginModuleのリファレンス・ドキュメントを参照できます。すべて、com.sun.security.authパッケージ内にあります。 - 
                              
フラグ: フラグ値は、前のLoginModuleの成功が
required、requisite、sufficientまたはoptionalであるかを示します。チュートリアルのように、存在するLoginModule固有のエントリが1つだけの場合、フラグを「required」にする必要があります。オプションは、Configurationクラスで詳しく説明しています。 - 
                              
LoginModuleオプション: 指定されたLoginModule実装でオプションの設定が可能な場合、ここで任意のオプション値を指定できます。空白で区切られた値リストは、基盤となるLoginModuleに直接渡されます。各オプションはLoginModule自身によって定義されており、内部の動作を制御します。たとえば、LoginModuleでデバッグ/テスト機能をサポートするオプションを定義する場合を考えましょう。
構成ファイルのオプションを指定する適正な方法は、名前-値ペアの使用です(たとえば
debug=true)。オプション名(この場合はdebug)と値(この場合はtrue)は、等号で区切ります。 
例6-2 required、sufficient、requisiteおよびoptionalのフラグを説明するログイン構成ファイル
次は、required、sufficient、requisiteおよびoptionalフラグを示すサンプル・ログイン構成ファイルです。これらのフラグの詳細は、Configurationクラスを参照してください。
                        
Login1 {
       sample.SampleLoginModule required debug=true;
    };
    Login2 {
       sample.SampleLoginModule required;
       com.sun.security.auth.module.NTLoginModule sufficient;
       com.foo.SmartCard requisite debug=true;
       com.foo.Kerberos optional debug=true;
    };アプリケーションLogin1は、構成済のLoginModuleの、SampleLoginModuleのみを保持します。このため、Login1がサブジェクト(ユーザーまたはサービス)を認証しようとする試みは、SampleLoginModuleが成功した場合にのみ成功します。
                        
アプリケーションLogin2の認証ロジックは、次の表で簡単に説明できます。
表6-1 Login2の認証ステータス
| モジュール・クラス | フラグ | 認証の試行1 | 認証の試行2 | 認証の試行3 | 認証の試行4 | 認証の試行5 | 認証の試行6 | 認証の試行7 | 認証の試行8 | 
|---|---|---|---|---|---|---|---|---|---|
| 
                                        
  | 
                                    
                                        required  | 
                                    
                                        pass  | 
                                    
                                        pass  | 
                                    
                                        pass  | 
                                    
                                        pass  | 
                                    
                                        fail  | 
                                    
                                        fail  | 
                                    
                                        fail  | 
                                    
                                        fail  | 
                                 
| 
                                        
  | 
                                    
                                        sufficient  | 
                                    
                                        pass  | 
                                    
                                        fail  | 
                                    
                                        fail  | 
                                    
                                        fail  | 
                                    
                                        pass  | 
                                    
                                        fail  | 
                                    
                                        fail  | 
                                    
                                        fail  | 
                                 
| 
                                        SmartCard  | 
                                    
                                        requisite  | 
                                    
                                        *  | 
                                    
                                        pass  | 
                                    
                                        pass  | 
                                    
                                        fail  | 
                                    
                                        *  | 
                                    
                                        pass  | 
                                    
                                        pass  | 
                                    
                                        fail  | 
                                 
| 
                                        Kerberos  | 
                                    
                                        オプション  | 
                                    
                                        *  | 
                                    
                                        pass  | 
                                    
                                        fail  | 
                                    
                                        *  | 
                                    
                                        *  | 
                                    
                                        pass  | 
                                    
                                        fail  | 
                                    
                                        *  | 
                                 
| 
                                        全体の認証  | 
                                    
                                        該当なし  | 
                                    
                                        pass  | 
                                    
                                        pass  | 
                                    
                                        pass  | 
                                    
                                        fail  | 
                                    
                                        fail  | 
                                    
                                        fail  | 
                                    
                                        fail  | 
                                    
                                        fail  | 
                                 
*=前のrequisiteモジュールが失敗するか、または前のsufficientモジュールが成功したため、アプリケーションに制御が返されるので、この値は微妙に変化します。
使用するログイン構成ファイルの位置指定
使用する構成ファイルは、次の2つのいずれかの方法で指定できます。
- 
                           
コマンド行。
-Djava.security.auth.login.configインタプリタ・コマンド行引数を使用して、使用すべきログイン構成ファイルを指定できます。この方法は、すべてのチュートリアルで使用されます。たとえば、JAAS認証チュートリアルでは、SampleAcnアプリケーションの実行に次のコマンドを使用します。このコマンドは、構成ファイルが現在のディレクトリのsample_jaas.configファイルであることを指定します。java -Djava.security.auth.login.config==sample_jaas.config sample.SampleAcnノート:
java.security.auth.login.configシステム・プロパティで、(等号を2つ(==)ではなく)等号を1つ(=)使用している場合、このシステム・プロパティおよびjava.securityファイルの両方で指定された構成が使用されます。 - 
                           
Javaセキュリティのプロパティ・ファイル。
ログイン構成ファイルの位置を指定する別の方法は、セキュリティ・プロパティ・ファイルの
login.config.url.nプロパティ値にURLを指定することです。セキュリティ・プロパティ・ファイルは、JDKのconf/securityディレクトリ内のjava.securityファイルです。ここで、
nは1から始まる連番の整数です。このため、必要に応じて複数のログイン構成ファイルを指定できます。この場合、login.config.url.1プロパティに最初のファイルのURLを、login.config.url.2プロパティに2番目のファイルのURLというように設定します。複数のログイン構成ファイルを指定する(つまり、n> 1の場合)、ファイルは読み取られて1つの構成に連結されます。ここで、このチュートリアルで使用する
sample_jaas.configログイン構成ファイルを指定するために、セキュリティ・プロパティ・ファイルに追加する必要のある項目の例を示します。この例は、ファイルが次のWindowsのC:\AcnTestディレクトリにあると仮定しています。login.config.url.1=file:C:/AcnTest/sample_jaas.configURLには、ユーザーの実行するオペレーティング・システムに関係なく、常にスラッシュを使用します。