この章では、Coherence*Extendクライアントにセキュリティを適用する方法を説明します。Coherence*Extendでは、Coherenceキャッシュに対する広範なアクセスが許可されます。これには、デスクトップ・アプリケーション、リモート・サーバーおよびWide Area Network(WAN)接続を介して配置されているマシンが含まれます。
この章の内容は、次のとおりです。
Coherence*Extendは、クラスタの外部で実行されるExtendクライアントと、1台以上のキャッシュ・サーバーでホストされ、クラスタ内で実行されるプロキシ・サービスで構成されます。クライアントAPIにより、すべてのリクエストがプロキシにルーティングされます。プロキシは、パーティション化またはレプリケートされたキャッシュ・サービス、起動サービスなどのCoherenceクラスタ・サービスに委任することによって、クライアント・リクエストに応答します。
Extendクライアントはクラスタの外部に存在するため、クラスタへのアクセス保護の問題が非常に重要になります。この章では、クライアントとクラスタ間のアクセス保護に使用可能な3つの技術を説明します。その技術とは、IDトークンベース・パスワード、資格が付与されるキャッシュ・サービスおよび起動サービスの使用などです。
これらのセキュリティ技術およびExtendクライアントの詳細な解説は、このチュートリアルの対象範囲ではありません。これらのトピックの詳細は、Oracle Coherenceセキュリティ・ガイドを参照してください。
トークンベースのセキュリティを実装すると、Extendクライアントとクラスタ内の拡張プロキシ間のアクセスを有効化できます。一般に、Extendクライアントと拡張プロキシ間のアクセスを有効化するには、次のファイルが必要です。
クライアント・アプリケーション・ファイル。クラスタにアクセスする機能を記述します。
キャッシュ構成ファイル。Extendクライアントと拡張プロキシのそれぞれに独自のキャッシュ構成がある場合。
オペレーション・オーバーライド・ファイル。デフォルトのオペレーショナル・デプロイメント・ディスクリプタの操作時および実行時の設定をオーバーライドします。
サーバー起動ファイル。拡張プロキシおよびクラスタ内のキャッシュ・サーバーに対して起動ファイルが存在する場合。
POF構成デプロイメント・ディスクリプタ。POFを使用してオブジェクトをシリアライズする際にカスタム・データ型を指定します。
トークンベース・セキュリティを追加するには、IDトランスフォーマおよびアサータの実装を指定する必要があります。このトランスフォーマは、クライアント側でトークンを生成し、アサータはクラスタ側でそれを検証します。
トークンベース・セキュリティを使用してクラスタにアクセスするExtendクライアントのアプリケーションを作成し、実行する手順は次のとおりです。
この章の例では、ロールベース・セキュリティ・ポリシーおよびキャッシュへのアクセス制御を定義するヘルパー・ファイルを参照します。これらの例に使用するための簡略マッピングのファイルが用意されています。
キャッシュ・アクセスはユーザーのロールによって決定されます。セキュリティ・ヘルパー・ファイルによっていくつかのロール(role_reader
、role_writer
およびrole_admin
)が定義されます。BuckarooBanzai
をROLE_ADMIN
にマッピングするなど、様々なユーザーのロールへのマッピングが定義されます。ROLE_ADMIN
を9
にマッピングするなど、ロールの整数IDへのマッピングが定義されます。また、ヘルパー・ファイルでは、例で使用されるキャッシュ名および起動サービス名も定義されます。
このファイルの主要機能は、login
およびcheckAccess
のメソッドです。login
メソッドは、ユーザー名を取得して、簡略化された識別名(DN)を構成します。その後、ロールを名前と関連付けます。PofPrincipalは、Principal
実装を提供します。
checkAccess
メソッドは、認可コードが配置されている場所を示します。これによって、提供されたユーザー・ロールに基づいてユーザーがキャッシュにアクセス可能であるかどうかが判定されます。
新規のプロジェクトおよびセキュリティ・ヘルパー・ファイルを作成するには:
Eclipse IDEで、Java EEパースペクティブを選択してSecurity
という名前の新規アプリケーション・クライアント・プロジェクトを作成します。「Configuration」ドロップダウン・リストから「CoherenceConfig」を選択します。「Application Client」モジュール・ページで、「Create a default main」が選択されていないことを確認します。
「Coherence」ページで、「Coherence37」のみを選択します。
プロジェクト作成の詳細は、「Eclipse IDEでの新規プロジェクトの作成」を参照してください。
SecurityExampleHelper.java
という名前で新規Javaファイルを作成します。パッケージ・パスがcom.oracle.handson
であることを確認します。
Javaクラスの作成の詳細は、「Javaクラスの作成」を参照してください。
例10-1に示されたコードをファイルにコピーします。
例10-1 セキュリティ・ヘルパー・ファイル
package com.oracle.handson; import com.tangosol.io.pof.PofPrincipal; import com.tangosol.net.security.SecurityHelper; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.security.Principal; import javax.security.auth.Subject; /** * This class provides extremely simplified role based policies and access control. * */ public class SecurityExampleHelper { // ----- static methods ------------------------------------------------- /** * Login the user. * * @param sName the user name * * @return the authenticated user */ public static Subject login(String sName) { // For simplicity, just create a Subject. Normally, this would be // done using JAAS. String sUserDN = "CN=" + sName + ",OU=Yoyodyne"; Set setPrincipalUser = new HashSet(); setPrincipalUser.add(new PofPrincipal(sUserDN)); // Map the user to a role setPrincipalUser.add(new PofPrincipal((String) s_mapUserToRole.get(sName))); return new Subject(true, setPrincipalUser, new HashSet(), new HashSet()); } /** * Assert that a Subject is associated with the calling thread with a * Principal representing the required role. * * @param sRoleRequired the role required for the operation * * @throws SecurityException if a Subject is not associated with the * calling thread or does not have the specified role Principal */ public static void checkAccess(String sRoleRequired) { checkAccess(sRoleRequired, SecurityHelper.getCurrentSubject()); } /** * Assert that a Subject contains a Principal representing the required * role. * * @param sRoleRequired the role required for the operation * * @param subject the Subject requesting access * * @throws SecurityException if a Subject is null or does not have the * specified role Principal */ public static void checkAccess(String sRoleRequired, Subject subject) { if (subject == null) { throw new SecurityException("Access denied, authentication required"); } Map mapRoleToId = s_mapRoleToId; Integer nRoleRequired = (Integer) mapRoleToId.get(sRoleRequired); for (Iterator iter = subject.getPrincipals().iterator(); iter.hasNext();) { Principal principal = (Principal) iter.next(); String sName = principal.getName(); if (sName.startsWith("role_")) { Integer nRolePrincipal = (Integer) mapRoleToId.get(sName); if (nRolePrincipal == null) { // invalid role break; } if (nRolePrincipal.intValue() >= nRoleRequired.intValue()) { return; } } } throw new SecurityException("Access denied, insufficient privileges"); } // ----- constants ----------------------------------------------------- public static final String ROLE_READER = "role_reader"; public static final String ROLE_WRITER = "role_writer"; public static final String ROLE_ADMIN = "role_admin"; /** * The cache name for security examples */ public static final String SECURITY_CACHE_NAME = "security"; /** * The name of the InvocationService used by security examples. */ public static String INVOCATION_SERVICE_NAME = "ExtendTcpInvocationService"; // ----- static data --------------------------------------------------- /** * The map keyed by user name with the value being the user's role. * Represents which user is in which role. */ private static Map s_mapUserToRole = new HashMap(); /** * The map keyed by role name with the value the role id. * Represents the numeric role identifier. */ private static Map s_mapRoleToId = new HashMap(); // ----- static initializer --------------------------------------------- static { // User to role mapping s_mapUserToRole.put("BuckarooBanzai", ROLE_ADMIN); s_mapUserToRole.put("JohnWhorfin", ROLE_WRITER); s_mapUserToRole.put("JohnBigboote", ROLE_READER); // Role to Id mapping s_mapRoleToId.put(ROLE_ADMIN, Integer.valueOf(9)); s_mapRoleToId.put(ROLE_WRITER, Integer.valueOf(2)); s_mapRoleToId.put(ROLE_READER, Integer.valueOf(1)); } }
IDトランスフォーマ(com.tangosol.net.security.IdentityTransformer
)は、クライアント側のコンポーネントで、サブジェクトまたはプリンシパルをIDトークンに変換します。このトークンは、Coherenceでシリアライズ可能なデータ型である必要があります。Coherenceは、実行時にこのトークンを自動的にシリアライズして、接続リクエストの一部としてプロキシに送信します。
IDトランスフォーマの実装を作成するには:
Security
プロジェクトに、PasswordIdentityTransformer
という名前のJavaクラスを新規作成します。
詳細は、「Javaクラスの作成」を参照してください。
IdentityTransformer
インタフェースをインポートします。PasswordIdentityTransformer
クラスがIdentityTransformer
インタフェースを実装することを確認します。
transformIdentity
メソッドを実装して、次のタスクを実行するようにします。
サブジェクトが存在し、完全であるかどうかのテスト
サブジェクトからのプリンシパル名の取得およびString
配列への保存
POFデータ型としてシリアライズ可能な、パスワードとプリンシパル名の組合せによるトークンの構成
例10-2は、考えられるPasswordIdentityTransformer
クラスの実装を示しています。
例10-2 IDトランスフォーマの実装例
package com.oracle.handson; import com.tangosol.net.security.IdentityTransformer; import java.security.Principal; import java.util.Iterator; import java.util.Set; import javax.security.auth.Subject; /** * PasswordIdentityTransformer creates a security token that contains the * required password and then adds a list of Principal names. * */ public class PasswordIdentityTransformer implements IdentityTransformer { // ----- IdentityTransformer interface ---------------------------------- /** * Transform a Subject to a token that asserts an identity. * * @param subject the Subject representing a user. * * @return the token that asserts identity. * * @throws SecurityException if the identity transformation fails. */ public Object transformIdentity(Subject subject, Service service) throws SecurityException { // The service is not needed so the service argument is being ignored. // It could be used, for example, if there were different token types // required per service. if (subject == null) { throw new SecurityException("Incomplete Subject"); } Set setPrincipals = subject.getPrincipals(); if (setPrincipals.isEmpty()) { throw new SecurityException("Incomplete Subject"); } String[] asPrincipalName = new String[setPrincipals.size() + 1]; int i = 0; asPrincipalName[i++] = System.getProperty("coherence.password", "secret-password"); for (Iterator iter = setPrincipals.iterator(); iter.hasNext();) { asPrincipalName[i++] = ((Principal) iter.next()).getName(); } // The token consists of the password plus the principal names as an // array of pof-able types, in this case strings. return asPrincipalName; } }
IDアサータ(com.tangosol.net.security.IdentityAsserter
)は、拡張プロキシ・サービスをホストするキャッシュ・サーバー上のクラスタ側のコンポーネントです。アサータは、ExtendクライアントでIDトランスフォーマによって作成されたトークンに、クラスタへのアクセスに必要な資格証明が格納されているかを検証します。
IDアサータの実装を作成するには:
Security
プロジェクトに、PasswordIdentityAsserter
という名前のJavaクラスを新規作成します。
詳細は、「Javaクラスの作成」を参照してください。
IdentityAsserter
インタフェースをインポートします。PasswordIdentityAsserter
クラスがIdentityAsserter
インタフェースを実装することを確認します。
次のタスクが実行されるように、assertIdentity
メソッドを実装します。
トークンに正しいパスワードが格納されていることおよびパスワードがトークン内の最初の名前であることを検証します。
例10-3は、考えられるPasswordIdentityAsserter
クラスの実装を示しています。
例10-3 IDアサータの実装例
package com.oracle.handson; import com.tangosol.io.pof.PofPrincipal; import com.tangosol.net.security.IdentityAsserter; import java.util.HashSet; import java.util.Set; import javax.security.auth.Subject; /** * PasswordIdentityAsserter asserts that the security token contains the * required password and then constructs a Subject based on a list of * Principal names. * */ public class PasswordIdentityAsserter implements IdentityAsserter { // ----- IdentityAsserter interface ------------------------------------- /** * Asserts an identity based on a token-based identity assertion. * * @param oToken the token that asserts identity. * * @return a Subject representing the identity. * * @throws SecurityException if the identity assertion fails. */ public Subject assertIdentity(Object oToken, Service service) throws SecurityException { // The service is not needed so the service argument is being ignored. // It could be used, for example, if there were different token types // required per service. if (oToken instanceof Object[]) { String sPassword = System.getProperty( "coherence.password", "secret-password"); Set setPrincipalUser = new HashSet(); Object[] asName = (Object[]) oToken; // first name must be password if (((String) asName[0]).equals(sPassword)) { // prints the user name to server shell to ensure we are // communicating with it and to ensure user is validated System.out.println("Password validated for user: " + asName[1]); for (int i = 1, len = asName.length; i < len; i++) { setPrincipalUser.add(new PofPrincipal((String)asName[i])); } return new Subject(true, setPrincipalUser, new HashSet(), new HashSet()); } } throw new SecurityException("Access denied"); } }
キャッシュの参照の取得時にパスワードを必要とするJavaファイルを作成します。SecurityExampleHelper.login("BuckarooBanzai")
を使用して、トークンを生成するSecurityExampleHelper
ファイルのlogin
メソッドをコールします。実行時に、ユーザー名がSecurityExampleHelper
クラスで定義されたサブジェクトと関連付けられます。トークンは、PasswordIdentityTransformer
クラスによってこのサブジェクトから生成され、PasswordIdentityAsserter
クラスによって接続リクエストの一部として検証されます。検証が成功すると、プロキシへの接続およびキャッシュの参照の権限が付与されます。Subject.doas
メソッドを使用して、セキュリティ・コンテキストでサブジェクトを利用できるようにします。
PasswordExample
という名前のSecurity
プロジェクトに、main
メソッドを使用して新たなJavaクラスを作成します。
詳細は、「Javaクラスの作成」を参照してください。
キャッシュの参照を取得するmain
メソッドを実装します。
SecurityExampleHelper.login
メソッドを使用して、ユーザーBuckarooBanzai
のサブジェクトを取得します。
doAs
メソッドを実装して、Javaセキュリティ・コンテキストのサブジェクト・パートを作成します。このサブジェクトは、後続のいずれのコードでも利用できます。この例では、定義されたロールを基準にして、ユーザーがキャッシュにアクセスできるかどうかを検証するためにdoAs
メソッドが実装されます。
例10-4は、考えられるPasswordExample
の実装を示しています。
例10-4 パスワード・サンプルを実行する実装例
package com.oracle.handson; import com.tangosol.net.CacheFactory; import com.tangosol.net.NamedCache; import java.io.IOException; import java.security.PrivilegedExceptionAction; import javax.security.auth.Subject; /** * This class shows how a Coherence Proxy can require a password to get a * reference to a cache. * <p> * The PasswordIdentityTransformer will generate a security token that * contains the password. The PasswordIdentityAsserter will validate the * security token to enforce the password. The token generation and * validation occurs automatically when a connection to the proxy is made. * */ public class PasswordExample { // ----- static methods ------------------------------------------------- /** * Get a reference to the cache. Password will be required. */ public static void main (String[] args){ getCache(); } public static void getCache() { System.out.println("------password example begins------"); Subject subject = SecurityExampleHelper.login("BuckarooBanzai"); try { NamedCache cache = (NamedCache) Subject.doAs( subject, new PrivilegedExceptionAction() { public Object run() throws Exception { NamedCache cache; cache = CacheFactory.getCache( SecurityExampleHelper.SECURITY_CACHE_NAME); System.out.println("------password example succeeded------"); return cache; } }); } catch (Exception e) { // get exception if the password is invalid System.out.println("Unable to connect to proxy"); e.printStackTrace(); } System.out.println("------password example completed------"); } }
IDトランスフォーマ(Extendクライアント上でSubject
をトークンに変換するクラス)およびIDアサータ(クラスタ上でトークンを検証するクラス)を定義するクラスを識別する、オペレーション・オーバーライド・ファイル(tangosol-coherence-override.xml
)を構成します。
「Project Explorer」ウィンドウから、tangosol-coherence-override.xml
ファイルを開きます。ファイルはSecurity/appClientModule
ディレクトリの下位にあります。
security-config
スタンザ内のidentity-transformer
要素とidentity-asserter
要素を使用して、PasswordIdentityTransformer
とPasswordIdentityAsserter
のそれぞれの実装クラスへのフルパスを識別します。subject-scope
パラメータをtrue
に設定して、現在のセキュリティ・コンテキストのIDをキャッシュおよびクライアントに返されるリモート起動サービス参照に関連付けます。
例10-5は、考えられるtangosol-coherence-override.xml
ファイルの実装を示しています。
例10-5 IDトランスフォーマとアサータの指定
<?xml version='1.0'?> <coherence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/coherence/coherence-operational-config" xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-operational-config coherence-operational-config.xsd" xml-override="/tangosol-coherence-override.xml"> <security-config> <identity-transformer> <class-name>com.oracle.handson.PasswordIdentityTransformer</class-name> </identity-transformer> <identity-asserter> <class-name>com.oracle.handson.PasswordIdentityAsserter</class-name> </identity-asserter> <subject-scope>true</subject-scope> </security-config> </coherence>
Extendクライアントのキャッシュ構成ファイルは、キャッシュ操作をクラスタ内の拡張プロキシにルーティングします。実行時に、キャッシュ操作はローカルで実行されるのではなく、拡張プロキシ・サービスに送信されます。
Extendクライアントのキャッシュ構成ファイルを作成するには:
「Project explorer」ウィンドウから、coherence-cache-config.xml
ファイルを開きます。ファイルはSecurity/appClientModule
ディレクトリの下位にあります。
ファイルをclient-cache-config.xml
という名前で保存します。
Extendクライアント・キャッシュ構成を作成します。次のリストは、主な要素の一部を取り上げています。
例10-6は、考えられるclient-cache-config.xml
ファイルの実装を示しています。
例10-6 Extendクライアント・キャッシュ構成ファイルのサンプル
<?xml version="1.0"?> <cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config" xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd"> <defaults> <serializer>pof</serializer> </defaults> <caching-scheme-mapping> <cache-mapping> <cache-name>security</cache-name> <scheme-name>examples-remote</scheme-name> </cache-mapping> </caching-scheme-mapping> <caching-schemes> <remote-cache-scheme> <scheme-name>examples-remote</scheme-name> <service-name>ExtendTcpCacheService</service-name> <initiator-config> <tcp-initiator> <remote-addresses> <socket-address> <address system-property="tangosol.coherence.proxy.address">localhost</address> <port system-property="tangosol.coherence.proxy.port">9099</port> </socket-address> </remote-addresses> </tcp-initiator> </initiator-config> </remote-cache-scheme> <remote-invocation-scheme> <scheme-name>remote-invocation-scheme</scheme-name> <service-name>ExtendTcpInvocationService</service-name> <initiator-config> <tcp-initiator> <remote-addresses> <socket-address> <address system-property="tangosol.coherence.proxy.address">localhost</address> <port system-property="tangosol.coherence.proxy.port">9099</port> </socket-address> </remote-addresses> <connect-timeout>2s</connect-timeout> </tcp-initiator> <outgoing-message-handler> <request-timeout>5s</request-timeout> </outgoing-message-handler> </initiator-config> </remote-invocation-scheme> </caching-schemes> </cache-config>
拡張プロキシ・サービスのキャッシュ構成ファイルを作成するには:
「Project explorer」ウィンドウから、coherence-cache-config.xml
ファイルを開きます。ファイルはSecurity/appClientModule
ディレクトリの下位にあります。
ファイルをexamples-cache-config.xml
という名前で保存します。
拡張プロキシ・キャッシュ構成ファイルを構成します。次のリストは、主な要素の一部を取り上げています。
cache-name
要素を使用して、キャッシュの名前としてsecurity
を定義します。Extendクライアントのキャッシュ構成でもsecurity
という名前のキャッシュが定義されている必要があります。
acceptor-config
スタンザのaddress
要素とport
要素を使用して、ポート9099
でlocalhost
アドレスのリスニングを行う拡張プロキシ・サービスを識別します。
システム・プロパティtangosol.coherence.extend.enabled
のautostart
要素を使用して、キャッシュ・サーバーでのプロキシ・サービスの実行を防止します。
defaults
と値pof
のserializer
を使用して、カスタムPOF構成ファイルのシリアライザ(この章の後述部分で作成します)をコールします。
例10-7は、考えられるexamples-cache-config.xml
ファイルの実装を示しています。
例10-7 プロキシ・サーバーのキャッシュ構成ファイルのサンプル
<?xml version="1.0"?> <cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config" xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd"> <defaults> <serializer>pof</serializer> </defaults> <caching-scheme-mapping> <cache-mapping> <cache-name>security</cache-name> <scheme-name>ExamplesPartitionedPofScheme</scheme-name> </cache-mapping> </caching-scheme-mapping> <caching-schemes> <distributed-scheme> <scheme-name>ExamplesPartitionedPofScheme</scheme-name> <service-name>PartitionedPofCache</service-name> <backing-map-scheme> <local-scheme> <!-- each node will be limited to 32MB --> <high-units>32M</high-units> <unit-calculator>binary</unit-calculator> </local-scheme> </backing-map-scheme> <autostart>true</autostart> </distributed-scheme> <!-- Proxy Service scheme that allows remote clients to connect to the cluster over TCP/IP. --> <proxy-scheme> <scheme-name>secure-proxy</scheme-name> <service-name>ProxyService</service-name> <thread-count system-property="tangosol.coherence.extend.threads">2</thread-count> <acceptor-config> <tcp-acceptor> <local-address> <address system-property="tangosol.coherence.extend.address">localhost</address> <port system-property="tangosol.coherence.extend.port">9099</port> </local-address> </tcp-acceptor> </acceptor-config> <autostart system-property="tangosol.coherence.extend.enabled">false</autostart> </proxy-scheme> </caching-schemes> </cache-config>
キャッシュ・サーバー・クラスタ・ノードの起動構成を作成します。この構成には、プロキシ・サービスおよびクラスタ側のキャッシュ構成ファイルを指定するシステム・プロパティを含める必要があります。また、クラス・パス上のアプリケーション・クラス・ファイルおよびXML構成ファイルも含める必要があります。
キャッシュ・サーバーの起動ファイルを作成するには:
Eclipseで、起動構成を作成します。Security
プロジェクトを右クリックして、「Run As」→「Run Configurations」を選択します。「Name」フィールドにSecurityCacheServer
と入力します。
「Main」タブの「Project」フィールドで「Browse」をクリックして「Security」プロジェクトを選択します。「Include system libraries when searching for a main class」を選択して、「Main class」フィールドの「Search」ボタンをクリックします。「Select Main Type」ダイアログ・ボックスの「Select type」フィールドにDefaultCacheServer
を入力します。「DefaultCacheServer - com.tangosol.net」を選択して「OK」をクリックします。「Apply」をクリックします。
「Coherence」タブに、クラスタ側のキャッシュ構成ファイルの名前と絶対パスを入力します(この例ではC:\home\oracle\workspace\Security\appClientModule\examples-cache-config.xml
)。「Local storage」フィールドで、「Enabled (cache server)」を選択します。「Cluster port」フィールドに3155
などの一意の値を入力します。「Apply」をクリックします。
「Classpath」タブで、「User Entries」→「Add External JARs」をクリックして、coherence.jarファイルをリストに追加します。「Apply」をクリックします。「Classpath」タブが図10-1のように表示されます。
注意: XML構成ファイル(C:\home\oracle\workspace\Security およびSecurity\build\classes フォルダ内)が、クラスパス上でcoherence.jar ファイルより前に表示されることを確認します。クラスローダーは、coherence.jar ファイル内での参照前に、カスタムPOF構成ファイル(この章の後述部分で作成し、pof-config.xml と命名する必要があります)を検出する必要があります。 |
「Common」タブの「Shared file」フィールドで、「Browse」をクリックしてSecurityプロジェクトを選択します。「Apply」をクリックします。
クラスタ内のキャッシュ・サーバー上で拡張プロキシサービスを起動する構成を作成します。Extendクライアントはこのサービスに接続します。この構成には、プロキシ・サービスおよびクラスタ側のキャッシュ構成ファイルを指定するシステム・プロパティを含める必要があります。また、クラス・パス上のアプリケーション・クラス・ファイルおよびXML構成ファイルも含める必要があります。
この例の場合、プロキシ・サービスの起動が構成されるキャッシュ・サーバーの構成は、キャッシュ・サーバーの起動構成と同じですが、拡張プロキシを有効にするシステム・プロパティが含まれます。
プロキシ・サービスを含むキャッシュ・サーバーの起動ファイルを作成するには:
Eclipseで、起動構成を作成します。Security
プロジェクトを右クリックして、「Run As」→「Run Configurations」を選択します。「Name」フィールドにSecurityRunProxy
と入力します。
「Main」タブの「Project」フィールドで「Browse」をクリックして「Security」プロジェクトを選択します。「Include system libraries when searching for a main class」を選択して、「Main class」フィールドの「Search」ボタンをクリックします。「Select Main Type」ダイアログ・ボックスの「Select type」フィールドにDefaultCacheServer
を入力します。「DefaultCacheServer - com.tangosol.net」を選択して「OK」をクリックします。「Apply」をクリックします。
「Coherence」タブに、クラスタ側のキャッシュ構成ファイルの名前と絶対パスを入力します(この例ではC:\home\oracle\workspace\Security\appClientModule\examples-cache-config.xml
)。「Local storage」フィールドで、「Enabled (cache server)」を選択します。「Cluster port」フィールドに3155
などの一意の値を入力します。「Apply」をクリックします。
「Arguments」タブで、システム・プロパティ-Dtangosol.coherence.extend.enabled=true
を入力して、「VM arguments」フィールドのプロキシ・サービスを指定します。
「Classpath」タブで、「User Entries」→「Add External JARs」をクリックして、coherence.jarファイルをリストに追加します。「Apply」をクリックします。「Classpath」タブが図10-3のように表示されます。
注意: XML構成ファイル(C:\home\oracle\workspace\Security およびSecurity\build\classes フォルダ内)が、クラスパス上でcoherence.jar ファイルより前に表示されることを確認します。クラスローダーは、coherence.jar ファイル内での参照前に、カスタムPOF構成ファイル(この章の後述部分で作成し、pof-config.xml と命名する必要があります)を検出する必要があります。 |
「Common」タブの「Shared file」フィールドで、「Browse」をクリックしてSecurityプロジェクトを選択します。「Apply」をクリックします。
パスワード・サンプルを実行して、トークンの生成、検証、プロキシ・サービスへの引渡しを行います。
PasswordExample.java
ファイルの実行構成を作成します。
「Project Explorer」でPasswordExample.java
を右クリックして、「Run As」→「Run Configurations」を選択します。
「New launch configuration」アイコンをクリックします。PasswordExample
が「Name」フィールドに、Security
が「Project」フィールドに、com.oracle.handson.PasswordExample
が「Main class」フィールドに表示されていることを確認します。「Apply」をクリックします。
「Coherence」タブの「Cache configuration descriptor」フィールドにクライアント・キャッシュ構成ファイルへのパスC:\home\oracle\workspace\Security\appClientModule\client-cache-config.xml
を入力します「Local cache」フィールドで、「Disabled (cache client)」を選択します。「Cluster port」フィールドに3155
などの一意の値を入力します。
「Classpath」タブで、「Add External JARs」をクリックして、coherence.jar
ファイルを「User Entries」に追加します。「Up」および「Down」ボタンを使用して、Securityフォルダを「User Entries」の先頭に移動します。終了すると、「Classpath」タブが図10-4のように表示されます。
実行中のキャッシュ・サーバーがあれば停止します。詳細は、「キャッシュ・サーバーの停止」を参照してください。
セキュリティ・プロキシ・サーバー、セキュリティ・キャッシュ・サーバー、続いてPasswordExample.java
プログラムを実行します。
プロジェクトを右クリックして、「Run As」→「Run Configurations」を選択します。「Run Configurations」ダイアログ・ボックスから、SecurityRunProxy
構成を実行します。
プロジェクトを右クリックして、「Run As」→「Run Configurations」を選択します。「Run Configurations」ダイアログ・ボックスから、SecurityCacheServer
構成を実行します。
「Project Explorer」でPasswordExample.java
ファイルを右クリックして、「Run As」→「Run Configurations」を選択します。「Run Configurations 」ダイアログ・ボックスでPasswordExampleを選択し、「Run」をクリックします。
Eclipseコンソールに、PasswordExample
プログラムの出力が例10-8のように表示されます。これにはパスワード・サンプルの開始、プロキシ・サーバーへのソケットのオープンおよびこの例の完了が示されます。
例10-8 Eclipseコンソールでのパスワード・サンプルの出力
------password example begins------
2011-03-15 11:37:48.484/0.296 Oracle Coherence 3.7.0.0 <Info> (thread=main, member=n/a): Loaded operational configuration from "jar:file:/C:/oracle/product/coherence/lib/coherence.jar!/tangosol-coherence.xml" 2011-03-15 11:37:48.531/0.343 Oracle Coherence 3.7.0.0 <Info> (thread=main, member=n/a): Loaded operational overrides from "jar:file:/C:/oracle/product/coherence/lib/coherence.jar!/tangosol-coherence-override-dev.xml" 2011-03-15 11:37:48.547/0.359 Oracle Coherence 3.7.0.0 <Info> (thread=main, member=n/a): Loaded operational overrides from "file:/C:/home/oracle/workspace/Security/build/classes/tangosol-coherence-override.xml" 2011-03-15 11:37:48.547/0.359 Oracle Coherence 3.7.0.0 <D5> (thread=main, member=n/a): Optional configuration override "/custom-mbeans.xml" is not specified Oracle Coherence Version 3.7.0.0 Build 22913 Grid Edition: Development mode Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. 2011-03-15 11:37:48.719/0.531 Oracle Coherence GE 3.7.0.0 <Info> (thread=main, member=n/a): Loaded cache configuration from "file:/C:/home/oracle/workspace/Security/appClientModule/client-cache-config.xml" 2011-03-15 11:37:48.844/0.656 Oracle Coherence GE 3.7.0.0 <Info> (thread=ExtendTcpCacheService:TcpInitiator, member=n/a): Loaded POF configuration from "jar:file:/C:/oracle/product/coherence/lib/coherence.jar!/pof-config.xml" 2011-03-15 11:37:48.875/0.687 Oracle Coherence GE 3.7.0.0 <Info> (thread=ExtendTcpCacheService:TcpInitiator, member=n/a): Loaded included POF configuration from "jar:file:/C:/oracle/product/coherence/lib/coherence.jar!/coherence-pof-config.xml" 2011-03-15 11:37:48.922/0.734 Oracle Coherence GE 3.7.0.0 <D5> (thread=ExtendTcpCacheService:TcpInitiator, member=n/a): Started: TcpInitiator{Name=ExtendTcpCacheService:TcpInitiator, State=(SERVICE_STARTED), ThreadCount=0, Codec=Codec(Format=POF), Serializer=com.tangosol.io.pof.ConfigurablePofContext, PingInterval=0, PingTimeout=30000, RequestTimeout=30000, ConnectTimeout=30000, SocketProvider=SystemSocketProvider, RemoteAddresses=[/130.35.99.213:9099], SocketOptions{LingerTimeout=0, KeepAliveEnabled=true, TcpDelayEnabled=false}} 2011-03-15 11:37:48.922/0.734 Oracle Coherence GE 3.7.0.0 <D5> (thread=main, member=n/a): Connecting Socket to 130.35.99.213:9099 2011-03-15 11:37:48.938/0.750 Oracle Coherence GE 3.7.0.0 <Info> (thread=main, member=n/a): Connected Socket to 130.35.99.213:9099------password example succeeded------
------password example completed------
プロキシ・サーバー・シェルの応答は、例10-9のように表示されます。これには、識別名からのCN
およびOU
の値およびパスワードが検証されたかどうかがリスト表示されます。
例10-9 プロキシ・サービス・シェルを実行するキャッシュ・サーバーからの応答
... Started DefaultCacheServer... 2011-03-15 11:37:33.797/21.250 Oracle Coherence GE 3.7.0.0 <D5> (thread=Cluster, member=1): Member(Id=2, Timestamp=2011-03-15 11:37:33.649, Address=130.35.99.213:8090, MachineId=49877, Location=site:us.oracle.com,machine:tpfaeffl-lap7,process:4160, Role=CoherenceServer) joined Cluster with senior member 1 2011-03-15 11:37:33.844/21.297 Oracle Coherence GE 3.7.0.0 <D5> (thread=Cluster, member=1): Member 2 joined Service Management with senior member 1 2011-03-15 11:37:34.313/21.766 Oracle Coherence GE 3.7.0.0 <D5> (thread=Cluster, member=1): Member 2 joined Service PartitionedPofCache with senior member 1 2011-03-15 11:37:34.344/21.797 Oracle Coherence GE 3.7.0.0 <D5> (thread=DistributedCache:PartitionedPofCache, member=1): 3> Transferring primary PartitionSet{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127} to member 2 requesting 128 2011-03-15 11:37:34.375/21.828 Oracle Coherence GE 3.7.0.0 <D4> (thread=DistributedCache:PartitionedPofCache, member=1): 1> Transferring 129 out of 129 partitions to a node-safe backup 1 at member 2 (under 129) 2011-03-15 11:37:34.406/21.859 Oracle Coherence GE 3.7.0.0 <D5> (thread=DistributedCache:PartitionedPofCache, member=1): Transferring 0KB of backup[1] for PartitionSet{128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256} to member 2Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne
Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne
Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne
この項では、クラスタへのアクセスにロールベースのポリシーを使用するサンプルを作成する方法を説明します。このコードは、特定のロールに割り当てられたユーザーIDを使用して、ログインし、サブジェクトを取得します。そのサブジェクトのコンテキストで実行中のキャッシュ参照を取得して、様々なキャッシュ操作を試行します。ユーザーに付与されたロールに応じて、キャッシュ操作が許可または拒否されます。このサンプルのロールのマッピングおよびロールベースの認可は簡略化されており、実際のセキュリティ保護での使用は想定していません。
たとえば、ライター・ロールのユーザーは、put
とget
のメソッドを使用できます。リーダー・ロールのユーザーは、get
メソッドを使用できますが、put
メソッドは使用できません。ライター・ロールのユーザーは、キャッシュを破棄できませんが、管理ロールのユーザーはできます。
サブジェクトのコンテキストでキャッシュ参照が作成されると、IDが永続的にその参照と関連付けられることに注意してください。キャッシュ参照の使用すべてがそのIDの代理として行われます。
この例では、前の項で作成したPasswordIdentityTransformer
クラスとPasswordIdentityAsserter
クラスを使用します。PasswordIdentityTransformer
クラスは、パスワード、ユーザーIDおよびロールを格納するセキュリティ・トークンを生成します。PasswordIdentityAsserter
クラス(プロキシで実行)は、セキュリティ・トークンを検証してパスワードを強制し、適切なユーザーIDとロールでサブジェクトを構成します。セキュリティ・トークンの生成とアサーションは自動的に実行されます。
サンプルを作成するには:
ユーザー・ロールを基準にして、キャッシュ・メソッドへのアクセスを可能にするJavaファイルを作成します。これは、Coherence*Extendを使用し、クライアントから渡されたSubject
オブジェクトを使用して、ラップされたNamedCache
にアクセス権を適用して実行できます。この実装では、指定されたロールのクライアントのみに、ラップされたNamedCache
へのアクセスが許可されます。
この項で作成するクラスは、com.tangosol.net.cache.WrapperNamedCache
クラスを拡張します。このクラスは、NamedCache
インタフェースのメソッドの保護を可能にする便利なファンクションです。
キャッシュ・メソッドへのアクセスが可能なユーザー・ロールを決定するため、キャッシュ・メソッドの実装のそれぞれにSecurityExampleHelper.checkAccess
メソッドのコールをインクルードします。checkAccess
に対する引数として、メソッドへのアクセスを許可するユーザー・ロールを指定します。super
のコールによって実装を終了します。
たとえば、次のコードでは、admin
ロールのユーザーがキャッシュを破棄できることが指定されます。
public void destroy() { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_ADMIN); super.destroy(); }
このサンプルでは、reader
ロールのユーザーにaggregate
メソッドのコールが許可されます。
public Object aggregate(Filter filter, EntryAggregator agent) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER); return super.aggregate(filter, agent); }
キャッシュ・メソッドをコールできるユーザーを決定するファイルを作成するには:
SecurityプロジェクトにEntitledNamedCache
という名前のJavaクラスを新規作成します。
詳細を確認する場合は、「Javaクラスの作成」を参照してください。
このクラスがWrapperNamedCache
をインポートして拡張することを確認します。
Filter
、MapListener
、ValueExtractor
、Collection
、Comparator
、Map
およびSet
のクラスをインポートします。WrapperNamedCache
内のメソッド(および拡張によるEntitledNamedCache
)ではこれらのデータ型の引数が使用されます。
特定のロールのユーザーのみにメソッドのコールが許可されるようにして、EntitledNamedCache
にメソッドを実装します。
例10-10は、考えられるEntitledNamedCache
の実装を示しています。
例10-10 資格が付与される名前付きキャッシュ
package com.oracle.handson; import com.tangosol.net.NamedCache; import com.tangosol.net.security.SecurityHelper; import com.tangosol.net.cache.WrapperNamedCache; import com.tangosol.util.Filter; import com.tangosol.util.MapEvent; import com.tangosol.util.MapListener; import com.tangosol.util.ValueExtractor; import java.util.Collection; import java.util.Comparator; import java.util.Map; import java.util.Set; import javax.security.auth.Subject; /** * Example WrapperNamedCache that demonstrates how entitlements can be applied * to a wrapped NamedCache using the Subject passed from the client through * Coherence*Extend. This implementation only allows clients with a specified * role to access the wrapped NamedCache. * */ public class EntitledNamedCache extends WrapperNamedCache { /** * Create a new EntitledNamedCache. * * @param cache the wrapped NamedCache */ public EntitledNamedCache(NamedCache cache) { super(cache, cache.getCacheName()); } // ----- NamedCache interface ------------------------------------------- /** * {@inheritDoc} */ public void release() { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER); super.release(); } /** * {@inheritDoc} */ public void destroy() { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_ADMIN); super.destroy(); } /** * {@inheritDoc} */ public Object put(Object oKey, Object oValue, long cMillis) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER); return super.put(oKey, oValue, cMillis); } /** * {@inheritDoc} */ public void addMapListener(MapListener listener) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER); super.addMapListener(new EntitledMapListener(listener)); } /** * {@inheritDoc} */ public void removeMapListener(MapListener listener) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER); super.removeMapListener(listener); } /** * {@inheritDoc} */ public void addMapListener(MapListener listener, Object oKey, boolean fLite) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER); super.addMapListener(new EntitledMapListener(listener), oKey, fLite); } /** * {@inheritDoc} */ public void removeMapListener(MapListener listener, Object oKey) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER); super.removeMapListener(listener, oKey); } /** * {@inheritDoc} */ public void addMapListener(MapListener listener, Filter filter, boolean fLite) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER); super.addMapListener(new EntitledMapListener(listener), filter, fLite); } /** * {@inheritDoc} */ public void removeMapListener(MapListener listener, Filter filter) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER); super.removeMapListener(listener, filter); } /** * {@inheritDoc} */ public int size() { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER); return super.size(); } /** * {@inheritDoc} */ public void clear() { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER); super.clear(); } /** * {@inheritDoc} */ public boolean isEmpty() { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER); return super.isEmpty(); } /** * {@inheritDoc} */ public boolean containsKey(Object oKey) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER); return super.containsKey(oKey); } /** * {@inheritDoc} */ public boolean containsValue(Object oValue) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER); return super.containsValue(oValue); } /** * {@inheritDoc} */ public Collection values() { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER); return super.values(); } /** * {@inheritDoc} */ public void putAll(Map map) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER); super.putAll(map); } /** * {@inheritDoc} */ public Set entrySet() { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER); return super.entrySet(); } /** * {@inheritDoc} */ public Set keySet() { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER); return super.keySet(); } /** * {@inheritDoc} */ public Object get(Object oKey) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER); return super.get(oKey); } /** * {@inheritDoc} */ public Object remove(Object oKey) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER); return super.remove(oKey); } /** * {@inheritDoc} */ public Object put(Object oKey, Object oValue) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER); return super.put(oKey, oValue); } /** * {@inheritDoc} */ public Map getAll(Collection colKeys) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER); return super.getAll(colKeys); } /** * {@inheritDoc} */ public boolean lock(Object oKey, long cWait) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER); return super.lock(oKey, cWait); } /** * {@inheritDoc} */ public boolean lock(Object oKey) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER); return super.lock(oKey); } /** * {@inheritDoc} */ public boolean unlock(Object oKey) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER); return super.unlock(oKey); } /** * {@inheritDoc} */ public Set keySet(Filter filter) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER); return super.keySet(filter); } /** * {@inheritDoc} */ public Set entrySet(Filter filter) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER); return super.entrySet(filter); } /** * {@inheritDoc} */ public Set entrySet(Filter filter, Comparator comparator) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER); return super.entrySet(filter, comparator); } /** * {@inheritDoc} */ public void addIndex(ValueExtractor extractor, boolean fOrdered, Comparator comparator) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER); super.addIndex(extractor, fOrdered, comparator); } /** * {@inheritDoc} */ public void removeIndex(ValueExtractor extractor) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER); super.removeIndex(extractor); } /** * {@inheritDoc} */ public Object invoke(Object oKey, EntryProcessor agent) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER); return super.invoke(oKey, agent); } /** * {@inheritDoc} */ public Map invokeAll(Collection collKeys, EntryProcessor agent) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER); return super.invokeAll(collKeys, agent); } /** * {@inheritDoc} */ public Map invokeAll(Filter filter, EntryProcessor agent) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER); return super.invokeAll(filter, agent); } /** * {@inheritDoc} */ public Object aggregate(Collection collKeys, EntryAggregator agent) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER); return super.aggregate(collKeys, agent); } /** * {@inheritDoc} */ public Object aggregate(Filter filter, EntryAggregator agent) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER); return super.aggregate(filter, agent); } // ----- inner class ---------------------------------------------------- /** * Example MapListener that adds authorization to map events. */ public class EntitledMapListener implements MapListener { // ----- constructors ------------------------------------------- /** * Construct an EntitledMapListener with the current subject. * The subject will not be available in the security context * when events are received by the proxy at runtime. * * @param listener the MapListener */ public EntitledMapListener(MapListener listener) { m_listener = listener; m_subject = SecurityHelper.getCurrentSubject(); } // ----- MapListener interface ---------------------------------- /** * {@inheritDoc} */ public void entryInserted(MapEvent mapEvent) { try { SecurityExampleHelper.checkAccess( SecurityExampleHelper.ROLE_WRITER, m_subject); } catch (SecurityException e) { System.out.println("Access denied for entryInserted"); return; } m_listener.entryInserted(mapEvent); } /** * {@inheritDoc} */ public void entryUpdated(MapEvent mapEvent) { try { SecurityExampleHelper.checkAccess( SecurityExampleHelper.ROLE_WRITER, m_subject); } catch (SecurityException e) { System.out.println("Access denied for entryUpdated"); return; } m_listener.entryUpdated(mapEvent); } /** * {@inheritDoc} */ public void entryDeleted(MapEvent mapEvent) { try { SecurityExampleHelper.checkAccess( SecurityExampleHelper.ROLE_WRITER, m_subject); } catch (SecurityException e) { System.out.println("Access denied for entryDeleted"); return; } m_listener.entryDeleted(mapEvent); } // ----- data members ------------------------------------------ /** * Subject from security context when the MapListener was registered */ private Subject m_subject; /** * Registered listener */ private MapListener m_listener; } // ----- helper methods ------------------------------------------------- /** * Return the wrapped NamedCache. * * @return the wrapped CacheService */ public NamedCache getNamedCache() { return (NamedCache) getMap(); } }
Coherence*Extendを使用し、クライアントから渡されたSubject
を使用して、ラップされたCacheService
にアクセス資格を適用する方法を示すファイルを作成します。この実装は、前の項で作成したEntitledNamedCache
にキャッシュ操作のアクセス制御を委譲します。
作成するクラスは、com.tangosol.net.WrapperCacheService
クラスを拡張します。これは、CacheService
のメソッドの保護を可能にする便利なファンクションです。また、プロキシ上のキャッシュ・サービスとクライアントのリクエスト間で委譲を行うメカニズムも提供します。
メソッドensureCache
、releaseCache
およびdestroyCache
を実装して、これらの使用が特定ロールのユーザーのみに許可されるように保証します。実装には、特定のユーザー・ロールを引数としてSecurityExampleHelper
.checkAccess
メソッドのコールをインクルードします。たとえば、次のコードでは、admin
ロールのユーザーのみにキャッシュの破棄が許可されることが保証されます。
public void destroyCache(NamedCache map) { if (map instanceof EntitledNamedCache) { EntitledNamedCache cache = (EntitledNamedCache) map; SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_ADMIN); map = cache.getNamedCache(); } super.destroyCache(map); }
キャッシュ・サービスへのアクセスに資格を適用するファイルを作成するには:
Security
プロジェクトにEntitledCacheService
という名前のJavaクラスを新規作成します。
このクラスがWrapperCacheService
クラスをインポートして拡張することを確認します。
ensureCache
、releaseCache
およびdestroyCache
のメソッドを実装して、これらの使用が特定ロールのユーザーのみに許可されるように保証します。
例10-11は、考えられるEntitledCacheService
の実装を示しています。
例10-11 資格が付与されるキャッシュ・サービス
package com.oracle.handson; import com.tangosol.net.CacheService; import com.tangosol.net.NamedCache; import com.tangosol.net.WrapperCacheService; /** * Example WrapperCacheService that demonstrates how entitlements can be * applied to a wrapped CacheService using the Subject passed from the * client through Coherence*Extend. This implementation delegates access control * for cache operations to the EntitledNamedCache. * */ public class EntitledCacheService extends WrapperCacheService { /** * Create a new EntitledCacheService. * * @param service the wrapped CacheService */ public EntitledCacheService(CacheService service) { super(service); } // ----- CacheService interface ----------------------------------------- /** * {@inheritDoc} */ public NamedCache ensureCache(String sName, ClassLoader loader) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER); return new EntitledNamedCache(super.ensureCache(sName, loader)); } /** * {@inheritDoc} */ public void releaseCache(NamedCache map) { if (map instanceof EntitledNamedCache) { EntitledNamedCache cache = (EntitledNamedCache) map; SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER); map = cache.getNamedCache(); } super.releaseCache(map); } /** * {@inheritDoc} */ public void destroyCache(NamedCache map) { if (map instanceof EntitledNamedCache) { EntitledNamedCache cache = (EntitledNamedCache) map; SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_ADMIN); map = cache.getNamedCache(); } super.destroyCache(map); } }
アクセス制御のサンプルを実行するファイルを作成します。ロール・ポリシーは、SecurityExampleHelper
クラスで定義されます。EntitledCacheService
クラスとEntitledNamedCache
クラスによってポリシーが強制されます。
このプログラムは、SecurityHelperFile.login
メソッドに対する引数として様々なユーザーを指定して、キャッシュの読取り、書込みおよび破棄の操作の実行を試行する必要があります。EntitledCacheService
クラスとEntitledNamedCache
クラスで定義された資格ポリシーに従って、操作の成功、または失敗が決まります。
AccessControlExample
という名前のSecurity
プロジェクトに、main
メソッドを使用して新たなJavaクラスを作成します。
詳細は、「Javaクラスの作成」を参照してください。
キャッシュにアクセスするmain
メソッドを実装します。
SecurityExampleHelper
ファイルで定義されたユーザーをlogin
メソッドの引数として指定します。
ユーザーごとに、キャッシュに対する読取り(get
)、書込み(put
)および destroy
操作を実行し、応答とする成功または失敗のメッセージを指定します。
例10-12は、考えられるAccessControlExample.java
クラスの実装を示しています。
例10-12 アクセス制御のサンプルを実行するサンプル・プログラム
package com.oracle.handson; import com.tangosol.net.CacheFactory; import com.tangosol.net.InvocationService; import com.tangosol.net.NamedCache; import com.tangosol.util.MapEvent; import com.tangosol.util.MapListener; import java.security.PrivilegedExceptionAction; import javax.security.auth.Subject; /** * This class demonstrates simplified role based access control. * <p> * The role policies are defined in SecurityExampleHelper. Enforcmenent * is done by EntitledCacheService and EntitledNamedCache. * */ public class AccessControlExample { // ----- static methods ------------------------------------------------- public static void main (String[] args){ accessCache(); } /** * Demonstrate role based access to the cache. */ public static void accessCache() { System.out.println("------cache access control example begins------"); Subject subject = SecurityExampleHelper.login("JohnWhorfin"); // Someone with writer role can write and read try { NamedCache cache = (NamedCache) Subject.doAs( subject, new PrivilegedExceptionAction() { public Object run() throws Exception { return CacheFactory.getCache(SecurityExampleHelper.SECURITY_CACHE_NAME); } }); cache.put("myKey", "myValue"); cache.get("myKey"); System.out.println(" Success: read and write allowed"); } catch (Exception e) { // get exception if not allowed to perform the operation e.printStackTrace(); } // Someone with reader role can read but not write subject = SecurityExampleHelper.login("JohnBigboote"); try { NamedCache cache = (NamedCache) Subject.doAs( subject, new PrivilegedExceptionAction() { public Object run() throws Exception { return CacheFactory.getCache(SecurityExampleHelper.SECURITY_CACHE_NAME); } }); cache.get("myKey"); System.out.println(" Success: read allowed"); cache.put("anotherKey", "anotherValue"); } catch (Exception e) { // get exception if not allowed to perform the operation System.out.println(" Success: Correctly cannot write"); } // Someone with writer role cannot call destroy subject = SecurityExampleHelper.login("JohnWhorfin"); try { NamedCache cache = (NamedCache) Subject.doAs( subject, new PrivilegedExceptionAction() { public Object run() throws Exception { return CacheFactory.getCache(SecurityExampleHelper.SECURITY_CACHE_NAME); } }); cache.destroy(); } catch (Exception e) { // get exception if not allowed to perform the operation System.out.println(" Success: Correctly cannot " + "destroy the cache"); } // Someone with admin role can call destroy subject = SecurityExampleHelper.login("BuckarooBanzai"); try { NamedCache cache = (NamedCache) Subject.doAs( subject, new PrivilegedExceptionAction() { public Object run() throws Exception { return CacheFactory.getCache(SecurityExampleHelper.SECURITY_CACHE_NAME); } }); cache.destroy(); System.out.println(" Success: Correctly allowed to " + "destroy the cache"); } catch (Exception e) { // get exception if not allowed to perform the operation e.printStackTrace(); } System.out.println("------cache access control example completed------"); } }
クラスタ側のキャッシュ構成ファイルexamples-cache-config.xml
を編集します。cache-service-proxy
スタンザのproxy-config
で、キャッシュ・サービスのクラス名のフルパスを指定します。cache-service-proxy
スタンザには、プロキシ・サービスによって管理されるキャッシュ・サービス・プロキシの構成情報が格納されます。
この例のキャッシュ・サービス・クラス名はcom.oracle.handson.EntitledCacheService
プロキシで、param-type
は、com.tangosol.net.CacheService
です。
例10-13は、構成に追加するXMLコードを示しています。
例10-13 クラスタ側のキャッシュ構成のキャッシュ・サービス・プロキシ構成
... <proxy-config> <cache-service-proxy> <class-name>com.oracle.handson.EntitledCacheService</class-name> <init-params> <init-param> <param-type>com.tangosol.net.CacheService</param-type> <param-value>{service}</param-value> </init-param> </init-params> </cache-service-proxy> </proxy-config> ...
アクセス制御サンプルを実行し、ユーザーのロールを基準にしてキャッシュへのアクセスがどのように許可または拒否されるかを示します。
AccessControlExample.java
の実行構成を作成します。
「Project Explorer」で、AccessControlExample
を右クリックします。「Run As」→「Run Configurations」を選択します。
「Oracle Coherence」→「New launch configuration」アイコンを選択します。AccessControlExample
が「Name」フィールドに、Security
が「Project」フィールドに、com.oracle.handson.AccessControlExample
が「Main class」フィールドに表示されていることを確認します。「Apply」をクリックします。
「Coherence」タブの「Cache configuration descriptor」フィールドにclient-cache-config.xml
ファイルへのパスを入力します。「Local cache」フィールドで、「Disabled (cache client)」を選択します。
「Classpath」タブで、「Add External JARs」をクリックして、coherence.jar
ファイルを「User Entries」に追加します。「Classpath」タブが図10-5のように表示されます。「Apply」→「Close」をクリックします。
実行中のキャッシュ・サーバーがあれば停止します。詳細は、「キャッシュ・サーバーの停止」を参照してください。
セキュリティ・プロキシ・サーバー、セキュリティ・キャッシュ・サーバー、続いてAccessControlExample.java
プログラムを実行します。
プロジェクトを右クリックして、「Run As」→「Run Configurations」を選択します。「Run Configurations」ダイアログ・ボックスから、SecurityRunProxy
構成を実行します。
プロジェクトを右クリックして、「Run As」→「Run Configurations」を選択します。「Run Configurations」ダイアログ・ボックスから、SecurityCacheServer
構成を実行します。
「Project Explorer」でAccessControl
Example.java
ファイルを右クリックして、「Run As」→「Run Configurations」を選択します。「Run Configurations」ダイアログ・ボックスから、AccessControl
Example
構成を実行します。
Eclipseコンソールに、例10-14のような出力が表示されます。メッセージは、AccessControlExample
で指定され、キャッシュの読取り、書込みおよび破棄操作を実行する様々なユーザーに対応します。
メッセージ「Success: read and write allowed
」は、キャッシュの読取りおよび書込みを試行するロールwriter
のユーザーに対応します。
メッセージ「Success: read allowed
」は、キャッシュの読取りを試行するロールreader
のユーザーに対応します。
メッセージ「Success: Correctly cannot write
」は、キャッシュへの書込みを試行するロールreader
のユーザーに対応します。
メッセージ「Success: Correctly cannot destroy the cache
」は、キャッシュの破棄を試行するロールwriter
のユーザーに対応します。
メッセージ「Success: Correctly allowed to destroy the cache
」は、キャッシュの破棄を試行するロールadmin
のユーザーに対応します。
例10-14 Eclipseコンソールのアクセス制御サンプルの出力
------cache access control example begins------
2011-03-15 14:05:12.953/0.313 Oracle Coherence 3.7.0.0 <Info> (thread=main, member=n/a): Loaded operational configuration from "jar:file:/C:/oracle/product/coherence/lib/coherence.jar!/tangosol-coherence.xml" 2011-03-15 14:05:13.000/0.360 Oracle Coherence 3.7.0.0 <Info> (thread=main, member=n/a): Loaded operational overrides from "jar:file:/C:/oracle/product/coherence/lib/coherence.jar!/tangosol-coherence-override-dev.xml" 2011-03-15 14:05:13.000/0.360 Oracle Coherence 3.7.0.0 <Info> (thread=main, member=n/a): Loaded operational overrides from "file:/C:/home/oracle/workspace/Security/appClientModule/tangosol-coherence-override.xml" 2011-03-15 14:05:13.000/0.360 Oracle Coherence 3.7.0.0 <D5> (thread=main, member=n/a): Optional configuration override "/custom-mbeans.xml" is not specified Oracle Coherence Version 3.7.0.0 Build 22913 Grid Edition: Development mode Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. 2011-03-15 14:05:13.156/0.516 Oracle Coherence GE 3.7.0.0 <Info> (thread=main, member=n/a): Loaded cache configuration from "file:/C:/home/oracle/workspace/Security/appClientModule/client-cache-config.xml" 2011-03-15 14:05:13.281/0.641 Oracle Coherence GE 3.7.0.0 <Info> (thread=ExtendTcpCacheService:TcpInitiator, member=n/a): Loaded POF configuration from "jar:file:/C:/oracle/product/coherence/lib/coherence.jar!/pof-config.xml" 2011-03-15 14:05:13.312/0.672 Oracle Coherence GE 3.7.0.0 <Info> (thread=ExtendTcpCacheService:TcpInitiator, member=n/a): Loaded included POF configuration from "jar:file:/C:/oracle/product/coherence/lib/coherence.jar!/coherence-pof-config.xml" 2011-03-15 14:05:13.359/0.719 Oracle Coherence GE 3.7.0.0 <D5> (thread=ExtendTcpCacheService:TcpInitiator, member=n/a): Started: TcpInitiator{Name=ExtendTcpCacheService:TcpInitiator, State=(SERVICE_STARTED), ThreadCount=0, Codec=Codec(Format=POF), Serializer=com.tangosol.io.pof.ConfigurablePofContext, PingInterval=0, PingTimeout=30000, RequestTimeout=30000, ConnectTimeout=30000, SocketProvider=SystemSocketProvider, RemoteAddresses=[/130.35.99.213:9099], SocketOptions{LingerTimeout=0, KeepAliveEnabled=true, TcpDelayEnabled=false}} 2011-03-15 14:05:13.375/0.735 Oracle Coherence GE 3.7.0.0 <D5> (thread=main, member=n/a): Connecting Socket to 130.35.99.213:9099 2011-03-15 14:05:13.375/0.735 Oracle Coherence GE 3.7.0.0 <Info> (thread=main, member=n/a): Connected Socket to 130.35.99.213:9099Success: read and write allowed
2011-03-15 14:05:13.484/0.844 Oracle Coherence GE 3.7.0.0 <D5> (thread=ExtendTcpCacheService:TcpInitiator, member=n/a): Started: TcpInitiator{Name=ExtendTcpCacheService:TcpInitiator, State=(SERVICE_STARTED), ThreadCount=0, Codec=Codec(Format=POF), Serializer=com.tangosol.io.pof.ConfigurablePofContext, PingInterval=0, PingTimeout=30000, RequestTimeout=30000, ConnectTimeout=30000, SocketProvider=SystemSocketProvider, RemoteAddresses=[/130.35.99.213:9099], SocketOptions{LingerTimeout=0, KeepAliveEnabled=true, TcpDelayEnabled=false}} 2011-03-15 14:05:13.484/0.844 Oracle Coherence GE 3.7.0.0 <D5> (thread=main, member=n/a): Connecting Socket to 130.35.99.213:9099 2011-03-15 14:05:13.484/0.844 Oracle Coherence GE 3.7.0.0 <Info> (thread=main, member=n/a): Connected Socket to 130.35.99.213:9099Success: read allowed
Success: Correctly cannot write
Success: Correctly cannot destroy the cache
2011-03-15 14:05:13.546/0.906 Oracle Coherence GE 3.7.0.0 <D5> (thread=ExtendTcpCacheService:TcpInitiator, member=n/a): Started: TcpInitiator{Name=ExtendTcpCacheService:TcpInitiator, State=(SERVICE_STARTED), ThreadCount=0, Codec=Codec(Format=POF), Serializer=com.tangosol.io.pof.ConfigurablePofContext, PingInterval=0, PingTimeout=30000, RequestTimeout=30000, ConnectTimeout=30000, SocketProvider=SystemSocketProvider, RemoteAddresses=[/130.35.99.213:9099], SocketOptions{LingerTimeout=0, KeepAliveEnabled=true, TcpDelayEnabled=false}} 2011-03-15 14:05:13.546/0.906 Oracle Coherence GE 3.7.0.0 <D5> (thread=main, member=n/a): Connecting Socket to 130.35.99.213:9099 2011-03-15 14:05:13.546/0.906 Oracle Coherence GE 3.7.0.0 <Info> (thread=main, member=n/a): Connected Socket to 130.35.99.213:9099Success: Correctly allowed to destroy the cache
------cache access control example completed------
例10-15は、キャッシュ・サーバーでプロキシ・サービスが実行される場合のシェルの出力をリストしています。出力内のセキュリティの例外は、Eclipseコンソールのメッセージ「Success: Correctly cannot write
」および「Success: Correctly cannot destroy the cache
」に対応することに注意します。
例10-15 プロキシ・サービスを実行中のキャッシュ・サーバーの出力
Started DefaultCacheServer... 2011-03-15 14:04:53.296/20.781 Oracle Coherence GE 3.7.0.0 <D5> (thread=Cluster, member=1): Member(Id=2, Timestamp=2011-03-15 14:04:53.147, Address=130.35.99.213:8090, MachineId=49877, Location=site:us.oracle.com,machine:tpfaeffl-lap7,process:3456, Role=CoherenceServer) joined Cluster with senior member 1 2011-03-15 14:04:53.359/20.844 Oracle Coherence GE 3.7.0.0 <D5> (thread=Cluster, member=1): Member 2 joined Service Management with senior member 1 2011-03-15 14:04:53.796/21.281 Oracle Coherence GE 3.7.0.0 <D5> (thread=Cluster, member=1): Member 2 joined Service PartitionedPofCache with senior member 1 2011-03-15 14:04:53.859/21.344 Oracle Coherence GE 3.7.0.0 <D5> (thread=DistributedCache:PartitionedPofCache, member=1): 3> Transferring primary PartitionSet{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127} to member 2 requesting 128 2011-03-15 14:04:53.890/21.375 Oracle Coherence GE 3.7.0.0 <D4> (thread=DistributedCache:PartitionedPofCache, member=1): 1> Transferring 129 out of 129 partitions to a node-safe backup 1 at member 2 (under 129) 2011-03-15 14:04:53.906/21.391 Oracle Coherence GE 3.7.0.0 <D5> (thread=DistributedCache:PartitionedPofCache, member=1): Transferring 0KB of backup[1] for PartitionSet{128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256} to member 2Password validated for user: role_writer
Password validated for user: role_writer
Password validated for user: role_writer
Password validated for user: role_reader
Password validated for user: role_reader
Password validated for user: role_reader
2011-03-15 14:05:13.500/40.985 Oracle Coherence GE 3.7.0.0 <D5> (thread=Proxy:ProxyService:TcpAcceptorWorker:1, member=1): An exception occurred while processing a PutRequest for Service=Proxy:ProxyService:TcpAcceptor: java.lang.SecurityException: Access denied, insufficient privileges at com.oracle.handson.SecurityExampleHelper.checkAccess(SecurityExampleHelper.java:104) at com.oracle.handson.SecurityExampleHelper.checkAccess(SecurityExampleHelper.java:58) at com.oracle.handson.EntitledNamedCache.put(EntitledNamedCache.java:69) at com.tangosol.coherence.component.net.extend.proxy.NamedCacheProxy.put$Router(NamedCacheProxy.CDB:1) at com.tangosol.coherence.component.net.extend.proxy.NamedCacheProxy.put(NamedCacheProxy.CDB:2) at com.tangosol.coherence.component.net.extend.messageFactory.NamedCacheFactory$PutRequest.onRun(NamedCacheFactory.CDB:6) at com.tangosol.coherence.component.net.extend.message.Request.run(Request.CDB:4) at com.tangosol.coherence.component.net.extend.proxy.NamedCacheProxy.onMessage(NamedCacheProxy.CDB:11) at com.tangosol.coherence.component.net.extend.Channel$MessageAction.run(Channel.CDB:13) at java.security.AccessController.doPrivileged(Native Method) at javax.security.auth.Subject.doAs(Subject.java:337) at com.tangosol.coherence.component.net.extend.Channel.execute(Channel.CDB:29) at com.tangosol.coherence.component.net.extend.Channel.receive(Channel.CDB:26) at com.tangosol.coherence.component.util.daemon.queueProcessor.service.Peer$DaemonPool$WrapperTask.run(Peer.CDB:9) at com.tangosol.coherence.component.util.DaemonPool$WrapperTask.run(DaemonPool.CDB:32) at com.tangosol.coherence.component.util.DaemonPool$Daemon.onNotify(DaemonPool.CDB:63) at com.tangosol.coherence.component.util.Daemon.run(Daemon.CDB:42) at java.lang.Thread.run(Thread.java:619) 2011-03-15 14:05:13.515/41.000 Oracle Coherence GE 3.7.0.0 <D5> (thread=Proxy:ProxyService:TcpAcceptorWorker:0, member=1): An exception occurred while processing a DestroyCacheRequest for Service=Proxy:ProxyService:TcpAcceptor: java.lang.SecurityException: Access denied, insufficient privileges at com.oracle.handson.SecurityExampleHelper.checkAccess(SecurityExampleHelper.java:104) at com.oracle.handson.SecurityExampleHelper.checkAccess(SecurityExampleHelper.java:58) at com.oracle.handson.EntitledCacheService.destroyCache(EntitledCacheService.java:63) at com.tangosol.coherence.component.net.extend.messageFactory.CacheServiceFactory$DestroyCacheRequest.onRun(CacheServiceFactory.CDB:6) at com.tangosol.coherence.component.net.extend.message.Request.run(Request.CDB:4) at com.tangosol.coherence.component.net.extend.proxy.serviceProxy.CacheServiceProxy.onMessage(CacheServiceProxy.CDB:9) at com.tangosol.coherence.component.net.extend.Channel$MessageAction.run(Channel.CDB:13) at java.security.AccessController.doPrivileged(Native Method) at javax.security.auth.Subject.doAs(Subject.java:337) at com.tangosol.coherence.component.net.extend.Channel.execute(Channel.CDB:29) at com.tangosol.coherence.component.net.extend.Channel.receive(Channel.CDB:26) at com.tangosol.coherence.component.util.daemon.queueProcessor.service.Peer$DaemonPool$WrapperTask.run(Peer.CDB:9) at com.tangosol.coherence.component.util.DaemonPool$WrapperTask.run(DaemonPool.CDB:32) at com.tangosol.coherence.component.util.DaemonPool$Daemon.onNotify(DaemonPool.CDB:63) at com.tangosol.coherence.component.util.Daemon.run(Daemon.CDB:42) at java.lang.Thread.run(Thread.java:619) Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne
起動サービス・クラスタ・サービスを使用すると、Extendクライアントによるクラスタ上の起動可能オブジェクトの実行が可能になります。このサンプルは、ロールベース・ポリシーを使用して起動可能オブジェクトの実行が可能なユーザーをどのように決定できるかを示しています。
たとえば、ライター・ロールのユーザーは、起動可能オブジェクトを実行できます。リーダー・ロールのユーザーはできません。
このサンプルでは、クライアント・プログラムからコールできる起動可能オブジェクトを作成します。起動可能オブジェクトはシリアライズ可能であるため、POF構成ファイルにこれをリストする必要もあります。また、ユーザーのロールを基準にして、特定のユーザーがサービス上のメソッドを実行できるかどうかをテストする起動サービス・プログラムも作成します。
前出のサンプルと同様、この例でもPasswordIdentityTransformer
クラスを使用して、パスワード、ユーザーIDおよびロールが格納されるセキュリティ・トークンを使用します。パスワードを強制し、適正なユーザーIDとロールでサブジェクトを構成するセキュリティ・トークンの検証には、PasswordIdentityAsserter
(プロキシで実行)が使用されます。セキュリティ・トークンの生成とアサーションは自動的に実行されます。
サンプルを作成するには:
資格が付与された起動サービスによって使用される簡略な起動可能オブジェクトの実装を作成します。たとえば、整数の増分および戻りを実行する起動可能オブジェクトを作成できます。
起動可能オブジェクトを作成するには:
Security
プロジェクトにExampleInvocable
という名前のJavaクラスを新規作成します。
詳細は、「Javaクラスの作成」を参照してください。
Invocable
インタフェースとInvocationService
インタフェースをインポートします。このクラスはシリアライズ可能オブジェクトを処理するため、PortableObject
、PofReader
およびPofWriter
のクラスをインポートします。
ExampleInvocable
クラスがInvocable
およびPortableObject
を実装することを保証します。
ExampleInvocable
クラスを実装して、整数を増分し、結果を返します。
PofReader.readExternal
メソッドとPofWriter.writeExternal
メソッドを実装します。
例10-16は、考えられるExampleInvocable.java
の実装を示しています。
例10-16 起動可能オブジェクトのサンプル
package com.oracle.handson; import com.tangosol.io.pof.PortableObject; import com.tangosol.io.pof.PofReader; import com.tangosol.io.pof.PofWriter; import com.tangosol.net.Invocable; import com.tangosol.net.InvocationService; import java.io.IOException; /** * Invocable implementation that increments and returns a given integer. */ public class ExampleInvocable implements Invocable, PortableObject { // ----- constructors --------------------------------------------- /** * Default constructor. */ public ExampleInvocable() { } // ----- Invocable interface -------------------------------------- /** * {@inheritDoc} */ public void init(InvocationService service) { m_service = service; } /** * {@inheritDoc} */ public void run() { if (m_service != null) { m_nValue++; } } /** * {@inheritDoc} */ public Object getResult() { return new Integer(m_nValue); } // ----- PortableObject interface --------------------------------- /** * {@inheritDoc} */ public void readExternal(PofReader in) throws IOException { m_nValue = in.readInt(0); } /** * {@inheritDoc} */ public void writeExternal(PofWriter out) throws IOException { out.writeInt(0, m_nValue); } // ----- data members --------------------------------------------- /** * The integer value to increment. */ private int m_nValue; /** * The InvocationService that is executing this Invocable. */ private transient InvocationService m_service; }
この例は、アクセス制御が提供されるようにリモート起動サービスをラップする方法を示しています。Coherence*Extendを使用すると、クライアントから渡されるSubject
を使用して、ラップされるInvocationService
にアクセス資格を適用できます。この実装では、ラップされた起動サービスへのアクセスが指定されたロールのクライアントのみに許可されます。
作成するクラスは、com.tangosol.net.WrapperInvocationServiceクラスを拡張します。これは、InvocationService
のメソッドの保護を可能にする便利なファンクションです。また、プロキシ上の起動サービスとクライアントのリクエスト間で委譲を行うメカニズムも提供します。
資格が付与される起動サービスを作成するには:
Security
プロジェクトにEntitledInvocationService
という名前のJavaクラスを新規作成します。
Invocable
、InvocationObserver
、InvocationService
、WrapperInvocationService
、Map
およびSet
のインタフェースをインポートします。EntitledInvocationService
クラスがWrapperInvocationService
クラスを拡張することを確認します。
query
メソッドとexecute
メソッドを実装します。指定されたユーザー・ロール(このサンプルではROLE_WRITER
)がこれらの操作にアクセスできるかどうかを判別するため、この実装に、SecurityExampleHelper.checkAccess
メソッドのコールをインクルードします。
例10-17は、考えられるEntitledInvocationService.java
の実装を示しています。
例10-17 資格が付与される起動サービスのサンプル
package com.oracle.handson; import com.tangosol.net.Invocable; import com.tangosol.net.InvocationObserver; import com.tangosol.net.InvocationService; import com.tangosol.net.WrapperInvocationService; import java.util.Map; import java.util.Set; /** * Example WrapperInvocationService that demonstrates how entitlements can be * applied to a wrapped InvocationService using the Subject passed from the * client through Coherence*Extend. This implementation only allows clients with a * specified role to access the wrapped InvocationService. * */ public class EntitledInvocationService extends WrapperInvocationService { /** * Create a new EntitledInvocationService. * * @param service the wrapped InvocationService */ public EntitledInvocationService(InvocationService service) { super(service); } // ----- InvocationService interface ------------------------------------ /** * {@inheritDoc} */ public void execute(Invocable task, Set setMembers, InvocationObserver observer) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER); super.execute(task, setMembers, observer); } /** * {@inheritDoc} */ public Map query(Invocable task, Set setMembers) { SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER); return super.query(task, setMembers); } }
アクセス起動サービス・サンプルを実行するプログラムを作成します。このプログラムの目的は、SecurityExampleHelper
クラスで定義されている多様なユーザーが起動可能オブジェクトにアクセスして実行できるかどうかをテストすることです。ロールベース・ポリシーの強制は、EntitledInvocationService
クラスによって指定されます。
アクセス起動サービスのサンプルを実行するプログラムを作成するには:
AccessInvocationServiceExample.java
という名前のSecurity
プロジェクトに、main
メソッドを使用してJavaクラスを作成します。
詳細は、「Javaクラスの作成」を参照してください。
クラスの中から、ExampleInvocable
、CacheFactory
およびInvocationService
をインポートします。
accessInvocationService
メソッドを起動するmain
メソッドを実装します。
accessInvocationService
クラスを実装して、SecurityExampleHelper
クラスで定義された様々なユーザーが、サービスへのログイン、およびExampleInvocable
で定義されたオブジェクトの実行を試行できるようにします。SecurityExampleHelper.login
メソッドを使用して、起動可能サービスにそれぞれのユーザーがアクセスできるかどうかをテストします。
例10-18は、考えられるAccessInvocationServiceExample.java
の実装を示しています。
例10-18 アクセス起動サービス・サンプルを実行するサンプル・プログラム
package com.oracle.handson; import com.oracle.handson.ExampleInvocable; import com.tangosol.net.CacheFactory; import com.tangosol.net.InvocationService; import java.security.PrivilegedExceptionAction; import javax.security.auth.Subject; /** * This class demonstrates simplified role based access control for the * invocation service. * <p> * The role policies are defined in SecurityExampleHelper. Enforcmenent * is done by EntitledInvocationService. * */ public class AccessInvocationServiceExample { /** * Invoke the example * * @param asArg command line arguments (ignored in this example) */ public static void main(String[] asArg) { accessInvocationService(); } /** * Access the invocation service */ public static void accessInvocationService() { System.out.println("------InvocationService access control example " + "begins------"); // Someone with writer role can run invocables Subject subject = SecurityExampleHelper.login("JohnWhorfin"); try { InvocationService service = (InvocationService) Subject.doAs( subject, new PrivilegedExceptionAction() { public Object run() { return CacheFactory.getService( SecurityExampleHelper.INVOCATION_SERVICE_NAME); } }); service.query(new ExampleInvocable(), null); System.out.println(" Success: Correctly allowed to " + "use the invocation service"); } catch (Exception e) { // get exception if not allowed to perform the operation e.printStackTrace(); } // Someone with reader role cannot cannot run invocables subject = SecurityExampleHelper.login("JohnBigboote"); try { InvocationService service = (InvocationService) Subject.doAs( subject, new PrivilegedExceptionAction() { public Object run() { return CacheFactory.getService( SecurityExampleHelper.INVOCATION_SERVICE_NAME); } }); service.query(new ExampleInvocable(), null); } catch (Exception ee) { System.out.println(" Success: Correctly unable to " + "use the invocation service"); } System.out.println("------InvocationService access control example " + "completed------"); } }
exam
ples-cache-config.xml
ファイルを編集して、起動サービスへのフルパスをinvocation-service-proxy
スタンザのproxy-config
に追加します。invocation-service-proxy
スタンザには、プロキシ・サービスによって管理される起動サービス・プロキシの構成情報が格納されます。
このサンプルの起動サービス・クラス名はcom.oracle.handson.EntitledInvocationService
で、そのparam-type
は、com.tangosol.net.InvocationService
です。
例10-19 クラスタ側キャッシュの起動サービス・プロキシ構成
... <proxy-config> ... <invocation-service-proxy> <class-name>com.oracle.handson.EntitledInvocationService</class-name> <init-params> <init-param> <param-type>com.tangosol.net.InvocationService</param-type> <param-value>{service}</param-value> </init-param> </init-params> </invocation-service-proxy> </proxy-config> ...
ユーザー定義型としてExampleInvocable
を宣言するPOFファイルを作成します。
「Project Explorer」で、Security\appClientModule
のpof-config.xml
ファイルを探し、Eclipse IDEで開きます。
ユーザー定義型としてExampleInvocable
を宣言するコードを入力して、ファイルを保存します。
このファイルの内容は、例10-20のようになります。このファイルは、C:\home\oracle\workspace\Security\appClientModule
フォルダに保存されます。
例10-20 ExampleInvocableユーザー定義型のPOF構成ファイル
<?xml version="1.0"?> <pof-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/coherence/coherence-pof-config" xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-pof-config coherence-pof-config.xsd"> <user-type-list> <!-- coherence POF user types --> <include>coherence-pof-config.xml</include> <!-- com.tangosol.examples package --> <user-type> <type-id>1007</type-id> <class-name>com.oracle.handson.ExampleInvocable</class-name> </user-type> </user-type-list> <allow-interfaces>true</allow-interfaces> <allow-subclasses>true</allow-subclasses> </pof-config>
クラスローダーは、coherence.jar
ファイル内での参照前に、カスタムPOF構成ファイルpof-config.xml
を検出する必要があります。このようにされないと、カスタムPOF構成ファイルが無視され、coherence.jar
ファイル内のデフォルト・ファイルが使用されることになります。
C:\home\oracle\workspace\Security\appModule
内のXML構成ファイルが使用されることを保証するため、サーバーのクラスパスのBootstrap EntriesセクションからCoherence37ライブラリを削除します。また、coherence.jar
ファイルをUser EntriesセクションのSecurityフォルダの後に配置します。
「Project Explorer」でプロジェクトを右クリックして、「Run As」→「Run Configurations」を選択します。
SecurityRunProxyを選択します。「Classpath」タブで、「Bootstrap Entries」のリストからCoherence37を削除します。「Apply」をクリックします。
SecurityCacheServerを選択します。「Classpath」タブで、「Bootstrap Entries」のリストからCoherence37を削除します。「Apply」をクリックします。
終了すると、SecurityRunProxy
およびSecurityCacheServer
の「Classpath」タブが図10-6のように表示されます。
アクセス起動サービス・サンプルを実行し、ユーザーのロールを基準にして起動可能オブジェクトへのアクセスがどのように許可または拒否されるかを示します。
AccessInvocationServiceExample.java
ファイルの実行構成を作成します。
「Project Explorer」でAccessInvocationServiceExample.java
を右クリックして、「Run As」→「Run Configurations」を選択します。
「Oracle Coherence」→「New launch configuration」アイコンをクリックします。AccessInvocationServiceExample
が「Name」フィールドに、Security
が「Project」フィールドに、com.oracle.handson.AccessInvocationServiceExample.java
が「Main class」フィールドに表示されていることを確認します。「Apply」をクリックします。
「Coherence」タブの「Cache configuration descriptor」フィールドにclient-cache-config.xml
ファイルへのパスを入力します。「Local cache」フィールドで、「Disabled (cache client)」を選択します。
「Classpath」タブで、「Bootstrap Entries」リストからCoherence37を削除します。「Add External JARs」をクリックして、coherence.jar
ファイルを「User Entries」に追加します。Security
フォルダを、「User Entries」の先頭に移動し、その次にcoherence.jar
ファイルを移動します。終了すると、「Classpath」タブが図10-7のように表示されます。「Apply」→「Close」をクリックします。
図10-7 AccessInvocationServiceExampleプログラムの「Classpath」タブ
実行中のキャッシュ・サーバーがあれば停止します。詳細は、「キャッシュ・サーバーの停止」を参照してください。
セキュリティ・プロキシ・サーバー、セキュリティ・キャッシュ・サーバー、続いてAccessInvocationServiceExample.java
プログラムを実行します。
プロジェクトを右クリックして、「Run As」→「Run Configurations」を選択します。「Run Configurations」ダイアログ・ボックスから、SecurityRunProxy
構成を実行します。
プロジェクトを右クリックして、「Run As」→「Run Configurations」を選択します。「Run Configurations」ダイアログ・ボックスから、SecurityCacheServer
構成を実行します。
「Project Explorer」でAccessInvocationService
Example.java
ファイルを右クリックして、「Run As」→「Run Configurations」を選択します。「Run Configurations」ダイアログ・ボックスでAccessControl
Example
を選択し、「Run」をクリックします。
Eclipseコンソールに、例10-21のような出力が表示されます。このメッセージは、AccessInvocationServiceExample
クラスで指定され、起動可能オブジェクトExampleInvocable
を実行しようとするユーザーに対応します。
メッセージ「Success: Correctly allowed to use the invocation service
」は、ExampleInvocable
を実行しようとするロールwriter
のユーザーに対応します。
メッセージ「Success: Correctly unable to use the invocation service
」は、ExampleInvocable
を実行しようとするロールreader
のユーザーに対応します。
例10-21 Eclipseコンソールのクライアント・プログラムの応答
------InvocationService access control example begins------
2011-03-15 17:54:40.468/0.453 Oracle Coherence 3.7.0.0 <Info> (thread=main, member=n/a): Loaded operational configuration from "jar:file:/C:/oracle/product/coherence/lib/coherence.jar!/tangosol-coherence.xml" 2011-03-15 17:54:40.515/0.500 Oracle Coherence 3.7.0.0 <Info> (thread=main, member=n/a): Loaded operational overrides from "jar:file:/C:/oracle/product/coherence/lib/coherence.jar!/tangosol-coherence-override-dev.xml" 2011-03-15 17:54:40.515/0.500 Oracle Coherence 3.7.0.0 <Info> (thread=main, member=n/a): Loaded operational overrides from "file:/C:/home/oracle/workspace/Security/appClientModule/tangosol-coherence-override.xml" 2011-03-15 17:54:40.515/0.500 Oracle Coherence 3.7.0.0 <D5> (thread=main, member=n/a): Optional configuration override "/custom-mbeans.xml" is not specified Oracle Coherence Version 3.7.0.0 Build 22913 Grid Edition: Development mode Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. 2011-03-15 17:54:40.750/0.735 Oracle Coherence GE 3.7.0.0 <Info> (thread=main, member=n/a): Loaded cache configuration from "file:/C:/home/oracle/workspace/Security/appClientModule/client-cache-config.xml" 2011-03-15 17:54:40.968/0.953 Oracle Coherence GE 3.7.0.0 <Info> (thread=ExtendTcpInvocationService:TcpInitiator, member=n/a): Loaded POF configuration from "file:/C:/home/oracle/workspace/Security/appClientModule/pof-config.xml" 2011-03-15 17:54:40.984/0.969 Oracle Coherence GE 3.7.0.0 <Info> (thread=ExtendTcpInvocationService:TcpInitiator, member=n/a): Loaded included POF configuration from "jar:file:/C:/oracle/product/coherence/lib/coherence.jar!/coherence-pof-config.xml" 2011-03-15 17:54:41.062/1.047 Oracle Coherence GE 3.7.0.0 <D5> (thread=ExtendTcpInvocationService:TcpInitiator, member=n/a): Started: TcpInitiator{Name=ExtendTcpInvocationService:TcpInitiator, State=(SERVICE_STARTED), ThreadCount=0, Codec=Codec(Format=POF), Serializer=com.tangosol.io.pof.ConfigurablePofContext, PingInterval=0, PingTimeout=5000, RequestTimeout=5000, ConnectTimeout=2000, SocketProvider=SystemSocketProvider, RemoteAddresses=[/130.35.99.213:9099], SocketOptions{LingerTimeout=0, KeepAliveEnabled=true, TcpDelayEnabled=false}} 2011-03-15 17:54:41.078/1.063 Oracle Coherence GE 3.7.0.0 <D5> (thread=main, member=n/a): Connecting Socket to 130.35.99.213:9099 2011-03-15 17:54:41.078/1.063 Oracle Coherence GE 3.7.0.0 <Info> (thread=main, member=n/a): Connected Socket to 130.35.99.213:9099Success: Correctly allowed to use the invocation service
2011-03-15 17:54:41.140/1.125 Oracle Coherence GE 3.7.0.0 <D5> (thread=ExtendTcpInvocationService:TcpInitiator, member=n/a): Started: TcpInitiator{Name=ExtendTcpInvocationService:TcpInitiator, State=(SERVICE_STARTED), ThreadCount=0, Codec=Codec(Format=POF), Serializer=com.tangosol.io.pof.ConfigurablePofContext, PingInterval=0, PingTimeout=5000, RequestTimeout=5000, ConnectTimeout=2000, SocketProvider=SystemSocketProvider, RemoteAddresses=[/130.35.99.213:9099], SocketOptions{LingerTimeout=0, KeepAliveEnabled=true, TcpDelayEnabled=false}} 2011-03-15 17:54:41.156/1.141 Oracle Coherence GE 3.7.0.0 <D5> (thread=main, member=n/a): Connecting Socket to 130.35.99.213:9099 2011-03-15 17:54:41.156/1.141 Oracle Coherence GE 3.7.0.0 <Info> (thread=main, member=n/a): Connected Socket to 130.35.99.213:9099Success: Correctly unable to use the invocation service
------InvocationService access control example completed------
例10-22は、キャッシュ・サーバーでプロキシ・サービスが実行される場合のシェルの出力をリストしています。出力のセキュリティの例外は、ロールreader
のユーザーがExampleInvocable
を実行しようとした場合のEclipseコンソールのメッセージ「Success: Correctly unable to use the invocation service
」に対応することに注意します。
例10-22 Eclipseコンソールのプロキシ・サービスの応答
... Started DefaultCacheServer... 2011-03-15 17:54:08.375/37.750 Oracle Coherence GE 3.7.0.0 <D5> (thread=Cluster, member=1): Member(Id=2, Timestamp=2011-03-15 17:54:08.226, Address=130.35.99.213:8090, MachineId=49877, Location=site:us.oracle.com,machine:tpfaeffl-lap7,process:5340, Role=CoherenceServer) joined Cluster with senior member 1 2011-03-15 17:54:08.437/37.812 Oracle Coherence GE 3.7.0.0 <D5> (thread=Cluster, member=1): Member 2 joined Service Management with senior member 1 2011-03-15 17:54:08.890/38.265 Oracle Coherence GE 3.7.0.0 <D5> (thread=Cluster, member=1): Member 2 joined Service PartitionedPofCache with senior member 1 2011-03-15 17:54:08.921/38.296 Oracle Coherence GE 3.7.0.0 <D5> (thread=DistributedCache:PartitionedPofCache, member=1): 3> Transferring primary PartitionSet{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127} to member 2 requesting 128 2011-03-15 17:54:08.968/38.343 Oracle Coherence GE 3.7.0.0 <D4> (thread=DistributedCache:PartitionedPofCache, member=1): 1> Transferring 129 out of 129 partitions to a node-safe backup 1 at member 2 (under 129) 2011-03-15 17:54:09.000/38.375 Oracle Coherence GE 3.7.0.0 <D5> (thread=DistributedCache:PartitionedPofCache, member=1): Transferring 0KB of backup[1] for PartitionSet{128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256} to member 2Password validated for user: role_writer
Password validated for user: role_writer
Password validated for user: role_reader
Password validated for user: role_reader
2011-03-15 17:54:41.156/70.531 Oracle Coherence GE 3.7.0.0 <D5> (thread=Proxy:ProxyService:TcpAcceptorWorker:1, member=1): An exception occurred while processing a InvocationRequest for Service=Proxy:ProxyService:TcpAcceptor: java.lang.SecurityException: Access denied, insufficient privileges
at com.oracle.handson.SecurityExampleHelper.checkAccess(SecurityExampleHelper.java:104) at com.oracle.handson.SecurityExampleHelper.checkAccess(SecurityExampleHelper.java:58) at com.oracle.handson.EntitledInvocationService.query(EntitledInvocationService.java:50) at com.tangosol.coherence.component.net.extend.messageFactory.InvocationServiceFactory$InvocationRequest.onRun(InvocationServiceFactory.CDB:12) at com.tangosol.coherence.component.net.extend.message.Request.run(Request.CDB:4) at com.tangosol.coherence.component.net.extend.proxy.serviceProxy.InvocationServiceProxy.onMessage(InvocationServiceProxy.CDB:9) at com.tangosol.coherence.component.net.extend.Channel$MessageAction.run(Channel.CDB:13) at java.security.AccessController.doPrivileged(Native Method) at javax.security.auth.Subject.doAs(Subject.java:337) at com.tangosol.coherence.component.net.extend.Channel.execute(Channel.CDB:29) at com.tangosol.coherence.component.net.extend.Channel.receive(Channel.CDB:26) at com.tangosol.coherence.component.util.daemon.queueProcessor.service.Peer$DaemonPool$WrapperTask.run(Peer.CDB:9) at com.tangosol.coherence.component.util.DaemonPool$WrapperTask.run(DaemonPool.CDB:32) at com.tangosol.coherence.component.util.DaemonPool$Daemon.onNotify(DaemonPool.CDB:63) at com.tangosol.coherence.component.util.Daemon.run(Daemon.CDB:42) at java.lang.Thread.run(Thread.java:619)