15 WebLogic JMSのセキュリティの理解
スレッド・ベースおよびオブジェクトベースのセキュリティ・モデルを使用してWebLogic JMSリソースを保護する方法を学習します。
WebLogic JMSリソースの保護
WebLogic JMSでは、JMS宛先へのアクセスを制限することでJMSリソースを保護できます。
デフォルトでは、すべてのユーザーがWebLogicサーバーまたはWebLogicクラスタ内のJMSリソースにアクセスできます。これには、リモート・アクセス権を持つユーザー、WebLogicサーバーまたはWebLogicクラスタ自体で直接実行しているユーザーが含まれます。WebLogic JMS宛先へのアクセスを制限するには、ユーザーのシステム・リソースに対するセキュリティ・ポリシーを作成し、ユーザーに必要なロールを付与する必要があります。セキュリティ・ロールとポリシーの詳細は、「WebLogicリソースの保護の概要」を、JMSで使用できるポリシーについては「Java Messaging Service (JMS)リソース」を参照してください。
JMSセキュリティに関する用語
WebLogic JMSでは、オブジェクトベース・セキュリティ(OBS)またはスレッド・ベースのセキュリティを使用して、保護されたWebLogic JMS宛先へのアクセス時にチェックされるユーザーを決定します。
2つのセキュリティ・アプローチの違いを調査する前に、JMSセキュリティのコンテキストで使用する一般的な用語をいくつか理解します。
- サブジェクト: WebLogicアプリケーション内のユーザーを表すセキュリティ・オブジェクト。
- プリンシパルおよび資格証明: それぞれユーザーのユーザー名およびパスワード。
- 資格証明: ユーザーのユーザー名とパスワードの組合せを表すために使用されることがよくあります。
スレッド・ベースのセキュリティでは、保護されたWebLogic JMS宛先がチェックするサブジェクトは、現在の呼出し側のスレッドから暗黙的に派生します。オブジェクトベースのセキュリティでは、サブジェクトは、呼出し側がJMS呼出しに使用しているオブジェクトに格納されたサブジェクトから暗黙的に派生します。一般に、WebLogicのセキュリティはスレッド・ベースです。次の各項では、これら両方の方法について説明します。
クライアントおよびサーバーでのスレッド・ベースのセキュリティの理解
デフォルトでは、保護されているWebLogic JMSリソースへのアクセスには、スレッド・ベースのセキュリティが利用されます。これにより、EJB、WebアプリケーションおよびRMIに対するJava EEの一般的なセキュリティ・モデルと同等のWebLogic JMSセキュリティの動作が提供されます。
- チェック済では、現在のスレッド内に暗黙的に格納されたセキュリティ・サブジェクト/ロールを使用します。
- チェックなしでは、JMS javax.jms
createConnection()
またはcreateJMSContext()
コールに渡すことができるユーザー名とパスワードを使用します。つまり、(1)からのスレッドのサブジェクトは、アプリケーションがオプションでcreateConnection()
またはcreateJMSContext()
に渡すことができるユーザー名およびパスワードより優先されます。
サーバー・アプリケーションに対するスレッド・ベースのセキュリティ
サーバー側アプリケーションでは、EJBおよびWebアプリケーション・スレッドのサブジェクトまたはロールを設定する方法は複数あります。『Oracle Fusion Middleware WebLogicセキュリティ・サービスによるアプリケーションの開発』を参照してください。サーバー側のWebLogic JMS送信および消費コールのスレッド・ベースのJMSセキュリティ・チェック動作をオーバーライドするには、「サーバー・アプリケーションにおけるオブジェクトベースのセキュリティの理解」を参照してください。
クライアント・アプリケーションに対するスレッド・ベースのセキュリティ
クライアント・アプリケーションの場合は、クライアント・アプリケーションでJNDIコンテキストが作成されると、現在のスレッドのサブジェクトが生成され、スレッドに暗黙的に配置されます。JNDIコンテキストは、SECURITY_PRINCIPAL
およびContext.SECURITY_CREDENTIALS
プロパティを使用して、資格証明をオプションで指定できます。
java.util.Hashtable env = new Hashtable();
env.put(Context.PROVIDER_URL, url); // typical url: t3://example.com:7001
env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
env.put(Context.SECURITY_PRINCIPAL, "myusername");
env.put(Context.SECURITY_CREDENTIALS, "user_password");
javax.naming.InitialContext ic = new InitialContext(env); // throws an exception if user name/password is incorrect
// thread now implicitly has subject for the ic user name/password
ic.close();
// thread now has original subject from before the ic was created
クライアントが資格証明を指定せずにInitialContext
オブジェクトを作成する場合、次のようになります。
- 現在のスレッドにすでにあるサブジェクトは変更されていません。
- スレッドのサブジェクトがない場合は、匿名のサブジェクトであるとみなされます。
クライアント・プログラムが、InitialContext
オブジェクトの作成に使用したスレッドとは異なるスレッドにスレッドのサブジェクトを転送する必要がある場合、クライアント・プログラムはセキュリティAPIを使用してスレッドの現在のサブジェクトを格納し、その後このキャッシュされたサブジェクトを別のスレッドで使用できます。
// retrieve the subject that is implicitly store in the current thread
javax.security.auth.Subject subject =
weblogic.security.Security.getCurrentSubject();
...
// use the given subject to perform an action:
// if the action throws, return an exception
// if the action succeeds, return "OK"
static Object doSomethingAsSubject(javax.security.auth.Subject subject) {
try {
return weblogic.security.Security.runAs(subject,
new java.security.PrivilegedExceptionAction() {
public java.lang.Object run() throws Exception {
// do something or throw
return "OK";
}});
} catch (java.security.PrivilegedActionException e) {
return e;
} catch (Throwable t) {
return t;
}
}
この例のコード・パターンは、1つのJMSクライアントが同時に2つのドメインと通信するユースケースで、スレッドのサブジェクトを切り替えるためにも使用できます。「2つのWebLogicドメインと通信する1つのJMSクライアントのプログラミング・パターン」を参照してください。
javax.security.auth.Subject anon = new javax.security.auth.Subject();
クライアント側のWebLogic JMS送信および消費コールのスレッド・ベースのJMSセキュリティ・チェック動作をオーバーライドするには、「クライアントにおけるオブジェクトベースのセキュリティの理解」を参照してください。
オブジェクトベースのセキュリティの理解
WebLogic JMSクライアントでは、スレッド・ベースのセキュリティではなく、オブジェクトベースのセキュリティ(OBS)と呼ばれるより単純なセキュリティ・モデルをオプションで使用できます。このオプションはWebLogic 12.2.1.3で導入されたもので、マルチスレッド・クライアントでは、これを使用しないとスレッド間でスレッド・ベースのセキュリティ・サブジェクトを転送するための追加コードが必要になるため便利です。
次の各項では、オブジェクトベースのセキュリティを有効にする方法について説明します。
クライアントでのオブジェクトベースのセキュリティの有効化
オブジェクトベースのセキュリティ(OBS)を有効にすると、メッセージが送信され、コール元のスレッドの件名ではなく、JMSクライアントの初期化時に指定した資格証明に基づいてセキュリティ・チェックが消費されます。
OBSを有効にするには、OBS JNDI初期コンテキストを使用する必要があります。OBS初期コンテキストから取得されるOBS接続ファクトリを使用して作成されるWebLogic JMS送信者またはコンシューマは、デフォルトで、現在の送信者またはコンシューマ・スレッドに関連付けられたサブジェクトではなく、OBS初期コンテキストに関連付けられた資格証明を暗黙的に使用します。また、ユーザー名とパスワード資格証明がOBS接続ファクトリの標準のJMS createConnection()
またはcreateJMSContext()
コールにパラメータとして渡される場合、この新しい資格証明は、OBS初期コンテキストに関連付けられた資格証明よりも優先します。新しい資格証明を使用して、その接続またはJMSコンテキストで送信または消費を行います。
JMSクライアントの送信者およびコンシューマでOBSを有効にするステップ:
クライアントでのオブジェクトベースのセキュリティの制限
次に、OBSの制限事項のいくつかを示します。
- OBS初期コンテキストでは、
lookup()
コールのみをサポートし、それ以外の場合はNotSupported
例外をスローします。他のコールをサポートするコンテキストが必要な場合は、OBSを有効にしない2番目のコンテキストを作成します。 - OBS初期コンテキストは、WebLogicクライアントでのみサポートされ、WebLogicサーバーではサポートされません。ブリッジ、MDB、リソース参照などのWebLogic JMSサーバー機能と組み合せて、このようなコンテキストを使用しようとすると、例外がスローされます。これらのサーバー側JMS機能にはすでに、OBSに類似したセマンティクスを提供する独自のセキュリティ処理があるため、このような制限が存在します。
- OBS初期コンテキストが作成されると、初期コンテキストのユーザーは現在のスレッドに配置されません。これは
weblogic.jndi.WLInitialContextFactory
コンテキストとは異なります。
サーバー・アプリケーションでのオブジェクトベースのセキュリティの有効化
インバウンドおよびアウトバウンドJMSアプリケーションでオブジェクトベース・セキュリティ(OBS)を有効にする方法を学習します。
インバウンドJMSアプリケーションのオブジェクトベースのセキュリティ
- アプリケーション自体での指定。
ノート:
この方法の使用はお薦めしません。 - サービス自体での定義(メッセージング・ブリッジを使用すると、ユーザー名またはパスワードを構成できます)。
ノート:
この方法の使用はお薦めしません。 - アウトバウンドの場合と同様に、JMSリソースをJNDIにマップするJMSシステム・リソース・モジュールに外部JMSサーバーを使用して指定します。
- 資格証明が動的に構成可能であり、アプリケーション・ファイルまたは記述子ファイルにハードコードされていないことを確認します。
- ほぼすべてのインバウンドおよびアウトバウンドのユースケースに適用されるため、JMS資格証明を集中管理する方法として役立ちます。
アウトバウンドJMSアプリケーションのオブジェクトベースのセキュリティ
アウトバウンドWebLogic JMSコールを実行するサーバー・アプリケーションは、現在のスレッドで現在のセキュリティ・サブジェクトに優先するオブジェクトベースのセキュリティ・パターンを実現できます。これが機能するには、次のことを確認してください。
- JMS接続ファクトリの現在のJNDIロケーションをローカルJNDIにマップするには、次のようにします。
- JMSシステム・リソース・モジュールに外部JMSサーバーを構成します。
- 必要な権限を持つユーザーに対し外部JMSサーバーの資格証明を構成します。
- アプリケーション・コードで、JMSリソース参照を使用するか、JMS接続ファクトリのローカルJNDI名を参照するJMSコンテキストを挿入します。
ノート:
オブジェクトベースのセキュリティ・パターンやスレッドベースのセキュリティ・パターンが必要かどうかにかかわらず、サーバー・アプリケーションがリソース参照またはJMSコンテキスト・インジェクションを使用してJMS接続ファクトリを参照することは、一般的なベスト・プラクティスです。
前述の要件が満たされた後、WebLogicサーバーは、外部JMSサーバーに構成した資格証明を、特定のJMS接続ファクトリから発生したアウトバウンド送信コールまたは消費コールすべてにインジェクトします。
クロス・ドメイン・セキュリティの理解
デフォルトでは、WebLogic Serverのセキュリティはスレッド・ベースです。つまり、現在のスレッドに関連付けられたユーザーとして操作が実行されます。「クライアントおよびサーバーでのスレッド・ベースのセキュリティの理解」を参照してください。アプリケーションがJMSを使用して複数のドメインと通信するとき、そのアプリケーションは、正しいユーザーが使用されることを確認して、関係する各ドメインと通信する必要があります。
また、ドメイン境界を越えてWebLogic Serverインスタンス間の内部通信を保護するには、ドメイン間でクロス・ドメイン・セキュリティを確立する必要があります。クロス・ドメインのセキュリティ構成を使用して、WebLogic Serverは、クロス・ドメイン・ユーザーのセキュリティ・ロールを確立し、各ドメインのWebLogic資格証明マッピング・セキュリティ・プロバイダを使用してクロス・ドメイン・ユーザーが使用する資格証明を格納します。ドメインごとにクロス・ドメイン・セキュリティを有効にできます。クロス・ドメイン資格証明マッピングは、内部通信の保護が必要な各リモート・ドメインに構成される必要があります。クロス・ドメインのセキュリティ構成の詳細は、『Oracle WebLogic Serverセキュリティの管理』のクロス・ドメインのセキュリティ構成に関する項を参照してください。様々なクロス・ドメインのシナリオでクロス・ドメイン・セキュリティを使用するためのガイドラインが用意されています。「クロス・ドメインのセキュリティ・ガイドライン」を参照してください。
ノート:
示されている例は、セキュリティのパターンを説明するためのサンプル・コードです。JMSコーディングのベスト・プラクティスに準拠する実際のコードではありません。「構成のベスト・プラクティス」を参照してください。
クロス・ドメインのセキュリティ・ガイドライン
クロス・ドメインのセキュリティを構成するときは、次のガイドラインに従います。
-
メッセージドリブンBean (MDB)のデプロイメントと、MDBがリスニングしているJMS宛先が別のWebLogicドメインにある場合は、2つのドメイン間のクロス・ドメイン・セキュリティと外部JMSサーバーを組み合せた構成を検討する必要があります。詳細は、『Oracle WebLogic ServerメッセージドリブンBeanの開発』のクロス・ドメイン・セキュリティでのMDBの使用に関する項を参照してください。
-
アプリケーションまたはメッセージング・ブリッジが、複数のWebLogicドメインに関係するグローバル・トランザクションに参加している場合は、2つのドメイン間のクロス・ドメイン・セキュリティの構成を検討する必要があります。これは、QoSが一度きりの2つのWebLogicドメインのメッセージング・ブリッジに適用されます。詳細は、『Oracle WebLogic Server JTAアプリケーションの開発』のクロス・ドメイン・セキュリティの構成に関する項を参照してください
-
1つのWebLogicドメイン内のアプリケーションが、別のWebLogicドメインのWebLogic Server JMS分散宛先にアクセスする場合は、2つのドメイン間のクロス・ドメイン・セキュリティの構成を検討する必要があります。また、アプリケーションは、リモート宛先を参照するために、外部JMSサーバーを標準Java EEリソース参照と組み合せて使用する必要があります。
-
アプリケーションがWebLogic Server JMSストア・アンド・フォワードを使用して、1つのWebLogicドメインからもう1つのWebLogicドメインにメッセージを転送する場合は、2つのドメイン間のクロス・ドメイン・セキュリティの構成を検討する必要があります。『Oracle WebLogic Serverストア・アンド・フォワード・サービスの管理』のSAFとクロス・ドメイン・セキュリティに関する項を参照してください。
-
1つのJMSクライアントが複数のドメインと同時に通信するとき、場合によっては、同じスレッドを使用してアプリケーションが呼びかけるドメインに対応するように、アプリケーション・コードでユーザーの切り替えを管理する必要があります。「クライアント・アプリケーションに対するスレッド・ベースのセキュリティ」の
runAs helper
メソッドの説明を参照してください。 -
外部JMSサーバーをクロス・ドメイン・シナリオで使用しているときに、この機能をJava EEリソース参照、MDBまたはメッセージング・ブリッジと組み合せていない場合は、組込みヘルパーのAPIを使用して、セキュリティ資格証明の伝播を正しく処理できます。
ノート:
ベスト・プラクティスは、ヘルパーAPIではなく、Java EEリソース参照、MDBまたはメッセージング・ブリッジの使用です。
2つのWebLogicドメインと通信する1つのJMSクライアントのプログラミング・パターン
WebLogic Server上で実行していない単一JMSクライアントは、有効なスレッド・ベースのセキュリティ・サブジェクトをそれぞれに指定するときに、場合によっては2つ(以上)のWebLogicドメインと同時に通信する必要があります。
次に、2つのドメインと通信する際にセキュリティ・サブジェクトを正しく処理していないアプリケーションの例を示します。これは、次に引用したコードで示すように、1つのドメインのJMS宛先からリクエスト・メッセージを受信し、別のドメインの宛先にレスポンス・メッセージを送信しようとします。
// This sample code INCORRECTLY handles security subjects
// in a client that communicates between two domains. It is
// intended to forward JMS messages.
java.util.Hashtable env = new Hashtable();
env.put(Context.PROVIDER_URL, domain1_url); // typical url: t3://example.com:7001
env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
env.put(Context.SECURITY_PRINCIPAL, "mydomain1_username");
env.put(Context.SECURITY_CREDENTIALS, "mydomain1_password");
javax.naming.InitialContext ctx = new InitialContext(env);
// thread now implicitly has subject that is valid in domain1
final Destination reqDest = (Destination) ctx.lookup(reqDestJNDI);
final ConnectionFactory cf = (ConnectionFactory) ctx.lookup(reqCfJNDI);
final MessageConsumer consumer = cf.createContext().createConsumer(reqDest);
java.util.Hashtable env2 = new Hashtable();
env2.put(Context.PROVIDER_URL, domain2_url); // typical url: t3://example.com:7001
env2.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
env2.put(Context.SECURITY_PRINCIPAL, "mydomain2_username");
env2.put(Context.SECURITY_CREDENTIALS, "mydomain2_password");
javax.naming.InitialContext ctx2 = new InitialContext(env2);
// thread now implicitly has subject that is valid in domain2
final Destination resDest = (Destination) ctx2.lookup(resDestJNDI);
final ConnectionFactory cf2 = (ConnectionFactory) ctx.lookup(resCfJNDI);
// create JMS producer to send response msg to domain2 now
MessageProducer producer = cf2.createConext().createProducer(resDest);
do {
// !!The following operation may fail since the current thread has the subject that is only valid in domain2
// while the consumer tries to talk to domain1.
Message msg = consumer.receive(1000);
if (msg != null) {
// process msg and generate response message resMsg
producer.send(resMsg);
}
} while (msg != null);
前述の例では、コンシューマがdomain1
で通信しようとしたときに、現在のスレッドのサブジェクトはdomain2
のみで有効なため、receive
操作が失敗する場合があります。
この問題を解決するには、「クライアント・アプリケーションに対するスレッド・ベースのセキュリティ」で説明されているプログラミング・パターンをJMSクライアント・アプリケーション・コードで使用し、新しい初期コンテキストが作成されるたびにサブジェクトをキャッシュして、必要に応じてスレッドでリストアする必要があります。次のコードは、前のサンプル・コードに対して必要な変更を示しています。
java.util.Hashtable env = new Hashtable();
env.put(Context.PROVIDER_URL, domain1_url); // typical url: t3://example.com:7001
env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
env.put(Context.SECURITY_PRINCIPAL, "mydomain1_username");
env.put(Context.SECURITY_CREDENTIALS, "mydomain1_password");
javax.naming.InitialContext ctx = new InitialContext(env);
// thread now implicitly has subject that is valid in domain1
// retrieve the subject that is implicitly stored in the current thread
javax.security.auth.Subject domain1Subject = weblogic.security.Security.getCurrentSubject();
final Destination reqDest = (Destination) ctx.lookup(reqDestJNDI);
final ConnectionFactory cf = (ConnectionFactory) ctx.lookup(reqCfJNDI);
// create JMS consumer to receive from domain1 now
MessageConsumer consumer = cf.createContext().createConsumer(reqDest);
java.util.Hashtable env2 = new Hashtable();
env2.put(Context.PROVIDER_URL, domain2_url); // typical url: t3://example.com:7001
env2.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
env2.put(Context.SECURITY_PRINCIPAL, "mydomain2_username");
env2.put(Context.SECURITY_CREDENTIALS, "mydomain2_password");
javax.naming.InitialContext ctx2 = new InitialContext(env2);
// thread now implicitly has subject that is valid in domain2
final Destination resDest = (Destination) ctx2.lookup(resDestJNDI);
final ConnectionFactory cf2 = (ConnectionFactory) ctx.lookup(resCfJNDI);
// we can create JMS producer to send response msg to domain2 now
MessageProducer producer = cf2.createConext().createProducer(resDest);
do{
// use the given subject to perform an action:
try {
Message msg = (Message) weblogic.security.Security.runAs(domain1Subject,
new.java.security.PrivilegedExceptionAction() {
public java.lang.Object run()throws Exception {
return consumer.receive(1000);
}});
} catch (java.security.PrivilegedActionException e) {
// handle securty exception
} catch (Throwable t) {
// handle other throwables
}
// the current thread still has the credentials for domain2
if (msg ) {
// process msg and generate response message resMsg
producer.send(resMsg);
}
} while (msg != null);
2つのWebLogicドメイン間で外部JMSサーバーを使用するためのプログラミング・パターン
ノート:
この項は、Java EEリソース参照、メッセージング・ブリッジまたはMDBと組み合せて外部JMSサーバーを使用している場合には該当しません。可能であればベスト・プラクティスとしてこれらの機能を使用します。そうすれば、クロス・ドメイン・セキュリティがドメイン間で適切に構成されているかぎり、特別なケースのセキュリティ処理コードは必要ないためです。外部JMSサーバーを使用して、1つのWebLogicドメインからもう1つのWebLogicドメインにJMSリソースをマップできます。クライアントまたはJava EEアプリケーションが外部JMS宛先マッピングのローカルJNDI名を直接ルックアップするとき、参照によるルックアップが暗黙的に行われます。外部JMSサーバーで構成されているリモート資格証明は、この参照によるルックアップの際に自動的に(一時的に)スレッドに配置されます。ルックアップがクライアントに戻されると、ルックアップ前にスレッドに存在していたローカル・サブジェクトがスレッドによって再開されます。ただし、MDB、メッセージング・ブリッジまたはリソース参照も使用していないかぎり、外部JMSサーバーのリモート資格証明が、後続のJMS操作で暗黙に使用されることはありません。そのため、リモート・ドメインに対する後続のJMSコールでこれらの機能が使用されないと、リモート・ドメイン内のJMSリソースが保護されている場合に、アクセス拒否エラーが発生します。これは、外部JMS構成に正しい資格証明が含まれていても同様です。
次のコード例は、外部JMSサーバーを使用し、Java EEリソース参照、MDBまたはメッセージング・ブリッジを使用せずに、リモートJMSリソースにアクセスするときに、JMSDestinationAvailabilityHelper
APIを使用して、正しいリモート資格証明がスレッドに存在するようにしています。JMSDestinationAvailabilityHelper
の使用方法の詳細は、「JMS宛先の可用性ヘルパーAPIを使用した分散宛先に関する拡張プログラミング」を参照してください
Hashtable h = new Hashtable();
h.put(Context.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");
h.put(Context.PROVIDER_URL, url);
h.put(Context.SECURITY_PRINCIPAL, user);
h.put(Context.SECURITY_CREDENTIALS, password);
RegistrationHandle handle = JMSDestinationAvailabilityHelper.getInstance().register(h, destJNDI, new MyDAHelperListener());
final Context ctx = new InitialContext(h);
final Destination queue = (Destination) ctx.lookup(destJNDI);
final ConnectionFactory cf= (ConnectionFactory) ctx.lookup(cfJNDI);
handle.runAs(
new PrivilegedExceptionAction(){
public Object run()throws Exception{
JMSContext context = cf.createContext();
for(int i=0; i<msgcount ; i++){
String msg= text + i;
context.createProducer().send(queue,msg);
}
context.close();
return null;
}
}
);
ctx.close();
handle.unregister();
}
private class MyDAHelperListener implements DestinationAvailabilityListener {
public void onDestinationsAvailable(String destJNDIName, List<DestinationDetail> list) {
// no op for this particular example
}
public void onDestinationsUnavailable(String destJNDIName, List<DestinationDetail> list){
// no op for this particular example
}
public void onFailure(String destJNDIName, Exception exception) {
// no op for this particular example
}
}