ヘッダーをスキップ
Oracle® Coherenceチュートリアル
リリース3.6
B61373-01
  ドキュメント・ライブラリへ
ライブラリ
製品リストへ
製品
目次
目次
索引
索引

戻る
戻る
 
次へ
次へ
 

10 セキュリティの処理

この章では、Coherence*Extendクライアントにセキュリティを適用する方法について説明します。Coherence*Extendでは、より幅広いコンシューマによるCoherenceキャッシュへのアクセスが可能となります。これらのコンシューマには、デスクトップ・アプリケーション、リモート・サーバー、WANに接続されたマシンなどが含まれます。

この章の内容は次のとおりです。

10.1 概要

Coherence*Extendは、クラスタの外部で実行されるExtendクライアントと、クラスタの内部で実行され、1つ以上のキャッシュ・サーバーによってホストされるプロキシ・サービスで構成されます。クライアントAPIにより、すべてのリクエストはプロキシにルーティングされます。プロキシは、パーティション・キャッシュ・サービスやレプリケーション・キャッシュ・サービス、起動サービスなどのCoherenceクラスタ・サービスに委任することによってクライアント・リクエストに応答します。

Extendクライアントはクラスタの外部に存在するため、クラスタへのアクセスを保護することが重要な問題になります。この章では、クライアントとクラスタ間のアクセス保護に使用できる3つのテクニックを示します。これらは、IDのトークンベース・パスワードを使用するテクニック、資格のあるキャッシュ・サービスを使用するテクニック、起動サービスを使用するテクニックの3つです。

これらのセキュリティ・テクニックおよびExtendクライアントの詳しい説明は、このチュートリアルの範囲外となります。詳細は、『Oracle Coherenceクライアント・ガイド』を参照してください。

10.2 トークンベースのセキュリティの有効化

トークンベースのセキュリティを実装して、Extendクライアントとクラスタ内の拡張プロキシ間のアクセスを有効にできます。Extendクライアントと拡張プロキシ間のアクセスを有効にするには、一般的に次のファイルが必要です。

トークンベースのセキュリティを追加するには、さらにIDトランスフォーマおよびアサーション・プロバイダを実装する必要があります。トランスフォーマはクライアント側でトークンを生成し、アサーション・プロバイダはクラスタ側でこれを検証します。

次の手順では、クラスタへのアクセスにトークンベースのセキュリティを使用するExtendクライアント用アプリケーションを作成および実行する方法について説明します。

  1. セキュリティ・ヘルパー・ファイルの使用

  2. IDトランスフォーマの作成

  3. IDアサーション・プロバイダの作成

  4. パスワード・ファイルの作成

  5. IDトランスフォーマおよびアサーション・プロバイダの有効化

  6. Extendクライアント用キャッシュ構成ファイルの作成

  7. 拡張プロキシ用キャッシュ構成ファイルの作成

  8. プロキシ・サービスのあるキャッシュ・サーバー用起動ファイルの作成

  9. キャッシュ・サーバー用起動ファイルの作成

  10. プロジェクト・クラスパスおよびランタイム・プロパティの構成

  11. パスワード例の実行

10.2.1 セキュリティ・ヘルパー・ファイルの使用

この章に示す例では、ロールベースのセキュリティ・ポリシーおよびキャッシュへのアクセス制御を定義するセキュリティ・ヘルパー・ファイルを参照します。ここでは、マッピングを簡略化したファイルを提供します。

キャッシュ・アクセスは、ユーザーのロールにより決定されます。セキュリティ・ヘルパー・ファイルでは、role_readerrole_writerおよびrole_adminの各ロールを定義します。BuckarooBanzaiからROLE_ADMINなど様々なユーザーからロールへのマッピング、およびROLE_ADMINから9などロールから整数値IDへのマッピングも定義します。さらに、例で使用するキャッシュ名や起動サービス名も定義します。

このファイルの主な機能は、loginメソッドとcheckAccessメソッドです。loginメソッドは、ユーザー名を使用して簡略化された識別名(DN)を構築します。そして、ロールをその名前に関連付けます。PofPrincipalは、Principalを実装します。

checkAccessメソッドは、認証コードが配置される場所を示します。これにより、指定されたユーザー・ロールに基づいてキャッシュにアクセスできるかどうかが決まります。

新しいプロジェクトおよびセキュリティ・ヘルパー・ファイルを作成する手順は次のとおりです。

  1. JDeveloperで、Securityという新規プロジェクトを作成します。パッケージ・パスがcom.oracle.handsonであることを確認します。

    プロジェクト作成の詳細は、「既存アプリケーションでの新規プロジェクトの作成」を参照してください。

  2. SecurityExampleHelper.javaという新規Javaファイルを作成します。

    Javaクラスの作成の詳細は、「Javaクラスの作成」を参照してください。

  3. 例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)
            {
            Subject subject = SecurityHelper.getCurrentSubject();
     
            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));
            }
        }
    

10.2.2 IDトランスフォーマの作成

IDトランスフォーマ(com.tangosol.net.security.IdentityTransformer)は、サブジェクトまたはプリンシパルをIDトークンに変換するクライアント側のコンポーネントです。トークンは、Coherenceがシリアライズ方法を認識しているタイプである必要があります。Coherenceでは、実行時に自動的にトークンをシリアライズし、これを接続リクエストの一部としてプロキシに送信します。

IDトランスフォーマの実装を作成する手順は次のとおりです。

  1. SecurityプロジェクトにPasswordIdentityTransformerという新規Javaクラスを作成します。

    詳細は、「Javaクラスの作成」を参照してください。

  2. IdentityTransformerインタフェースをインポートします。PasswordIdentityTransformerIdentityTransformerが実装されていることを確認します。

  3. 次のタスクを実行するように、transformIdentityメソッドを実装します。

    • Subjectが存在し、完全であることをテストする。

    • Subjectからプリンシパル名を取得し、String配列に保存する。

    • トークンを、パスワードとPOF対応タイプのPrincipal名の組合せとして構築する。

例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)
            throws SecurityException
        {
        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;
        }
    }

10.2.3 IDアサーション・プロバイダの作成

IDアサーション・プロバイダ(com.tangosol.net.security.IdentityAsserter)は、拡張プロキシ・サービスをホストするキャッシュ・サーバー上にあるクラスタ側のコンポーネントです。アサーション・プロバイダは、ExtendクライアントでIDトランスフォーマにより作成されたトークンに、クラスタへのアクセスに必要な資格証明が含まれていることを検証します。

IDアサーション・プロバイダの実装を作成する手順は次のとおりです。

  1. SecurityプロジェクトにPasswordIdentityAsserterという新規Javaクラスを作成します。

    詳細は、「Javaクラスの作成」を参照してください。

  2. IdentityAsserterインタフェースをインポートします。PasswordIdentityAsserterIdentityAsserterが実装されていることを確認します。

  3. 次を実行するように、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)
            throws SecurityException
        {
        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");
        }
    }

10.2.4 パスワード・ファイルの作成

キャッシュへの参照の取得にパスワードを必要とするJavaファイルを作成します。SecurityExampleHelper.login("BuckarooBanzai")を使用してSecurityExampleHelper内のloginメソッドをコールし、トークンを生成します。実行時に、ユーザー名がSecurityExampleHelperファイルで定義されているそのサブジェクトと関連付けられます。PasswordIdentityTransformerによってこのサブジェクトからトークンが生成され、接続リクエストの一部としてPasswordIdentityAsserterにより検証されます。検証が成功すると、プロキシへの接続とキャッシュへの参照が承認されます。Subject.doasを使用して、Subjectをセキュリティ・コンテキストで有効にします。

  1. Securityプロジェクトで、mainメソッドを含むPasswordExampleという名前の新規Javaクラスを作成します。

    詳細は、「Javaクラスの作成」を参照してください。

  2. キャッシュへの参照を取得するmainを実装します。

  3. SecurityExampleHelper.loginを使用して、ユーザーBuckarooBanzaiのサブジェクトを取得します。

  4. 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------");
        }
     
    }

10.2.5 IDトランスフォーマおよびアサーション・プロバイダの有効化

オペレーション・オーバーライド・ファイル(tangosol-coherence-override.xml)を構成し、IDトランスフォーマ(ExtendクライアントでSubjectをトークンに変換するクラス)とIDアサーション・プロバイダ(クラスタでそのトークンを検証するクラス)を定義するクラスを指定します。

  1. JDeveloperで、XMLファイルを作成します。このファイルにtangosol-coherence-override.xmlという名前を付け、C:\home\oracle\labs\ディレクトリに保存します。

  2. security-config行内でidentity-transformer要素とidentity-asserter要素を使用して、それぞれPasswordIdentityTransformer実装クラスとPasswordIdentityAsserter実装クラスのフルパスを指定します。subject-scopetrueに設定し、現在のセキュリティ・コンテキストからのIDを、クライアントに返されるキャッシュおよびリモート起動サービスの参照と関連付けます。

例10-5に、tangosol-coherence-override.xmlの可能な実装を示します。

例10-5 IDトランスフォーマおよびアサーション・プロバイダの指定

<?xml version='1.0'?>
 
<!DOCTYPE coherence SYSTEM "coherence.dtd">
 
<coherence>
  <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>

10.2.6 Extendクライアント用キャッシュ構成ファイルの作成

Extendクライアントのキャッシュ構成ファイルは、キャッシュ処理をクラスタ内の拡張プロキシにルーティングします。実行時、キャッシュ処理はローカルに実行されるのではなく、拡張プロキシ・サービスに送信されます。

Extendクライアントのキャッシュ構成ファイルを作成する手順は次のとおりです。

  1. JDeveloperで、XMLファイルを作成します。このファイルにclient-cache-config.xmlという名前を付け、C:\home\oracle\labs\ディレクトリに保存します。

  2. Extendクライアントのキャッシュ構成を記述します。いくつかの主要な要素について次に説明します。

    • cache-name要素を使用して、キャッシュ名としてsecurityを定義する。クラスタ側のキャッシュ構成にもsecurityという名前のキャッシュが定義されている必要があります。

    • remote-cache-scheme行を使用して、リモート・キャッシュの詳細を定義する。

    • tcp-initiator行でaddress要素およびport要素を使用して、アドレスlocalhost、ポート9099でリスニングする拡張プロキシ・サービスを指定する。

    • defaultsおよびserializerを値pofで使用して、カスタムPOF構成ファイル(この章の後半で作成)のシリアライザをコールする。

例10-6に、client-cache-config.xmlの可能な実装を示します。

例10-6 Extendクライアント用キャッシュ構成ファイルのサンプル

<?xml version="1.0"?>
<!DOCTYPE cache-config SYSTEM "cache-config.dtd">
 
<cache-config>
  <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>

10.2.7 拡張プロキシ用キャッシュ構成ファイルの作成

拡張プロキシ・サービス用キャッシュ構成ファイルを作成します。

  1. JDeveloperで、XMLファイルを作成します。このファイルにexamples-cache-config.xmlという名前を付け、C:\home\oracle\labs\ディレクトリに保存します。

  2. 拡張プロキシのキャッシュ構成ファイルを構成します。いくつかの主要な要素について次に説明します。

    • cache-name要素を使用して、キャッシュ名としてsecurityを定義する。Extendクライアントのキャッシュ構成にもsecurityという名前のキャッシュが定義されている必要があります。

    • acceptor-config行でaddress要素およびport要素を使用して、アドレスlocalhost、ポート9099でリスニングする拡張プロキシ・サービスを指定する。

    • autostart要素をtangosol.coherence.extend.enabledシステム・プロパティで使用して、キャッシュ・サーバーがプロキシ・サービスを実行しないようにする。

    • defaultsおよびserializerを値pofで使用して、カスタムPOF構成ファイル(この章の後半で作成)のシリアライザをコールする。

例10-7に、examples-cache-config.xmlの可能な実装を示します。

例10-7 プロキシ・サーバー用キャッシュ構成ファイルのサンプル

<?xml version="1.0"?>
 
<!DOCTYPE cache-config SYSTEM "cache-config.dtd">
 
<cache-config>
  <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>
            <reusable>true</reusable>
          </local-address>
        </tcp-acceptor>
      </acceptor-config>
 
       <autostart system-property="tangosol.coherence.extend.enabled">false</autostart>
    </proxy-scheme>
  </caching-schemes>
</cache-config>

10.2.8 キャッシュ・サーバー用起動ファイルの作成

キャッシュ・サーバーのクラスタ・ノード用起動ファイルを作成します。起動ファイルには、プロキシ・サービスとクラスタ側のキャッシュ構成ファイルを指定するシステム・プロパティを含める必要があります。クラスパスに、アプリケーション・クラス・ファイルとXML構成ファイルを含める必要もあります。

キャッシュ・サーバー用起動ファイルを作成する手順は次のとおりです。

  1. JDeveloperで、起動ファイルを作成します。Securityプロジェクトを右クリックし、「新規」を選択します。「新規ギャラリ」で、「カテゴリ」フィールドの「一般」および「アイテム」フィールドの「ファイル」を選択します。「新規ファイル」ダイアログで、ファイル名としてsecurity-cache-server.cmdを入力し、Coherenceの\binディレクトリ(この場合はC:\oracle\product\coherence\bin)に保存します。

  2. コマンドを入力してキャッシュ・サーバーを起動します。主なコマンドは次のとおりです。

    • クラスタ側のキャッシュ構成ファイル(この場合はexamples-cache-config.xml)のパスを示すtangosol.coherence.cacheconfigシステム・プロパティ

    • coherence.jarC:\oracle\product\lib\coherence.jar)、Securityプロジェクト・クラス(C:\home\oracle\labs\Security\classes)、およびXML構成ファイル(C:\home\oracle\labsのパスを含めるよう定義されたクラスパス


    注意:

    クラスパスで、XML構成ファイル(C:\home\oracle\labsの下)はcoherence.jarより前に指定してください。クラス・ローダーは、coherence.jarでカスタムのPOF構成ファイル(この章の後半で作成)を参照する前に、この構成ファイルを見つける必要があります。

例10-8に、security-cache-server.cmdの可能な実装を示します。

例10-8 キャッシュ・サーバー用起動ファイル

@echo off
@
@rem This will start a cache server
@
setlocal
 
:config
@rem specify the Coherence installation directory
set coherence_home=c:\oracle\product\coherence
 
@rem specify the JVM heap size
set memory=512m
 
 
:start
if not exist "%coherence_home%\lib\coherence.jar" goto instructions
 
if "%java_home%"=="" (set java_exec=java) else (set java_exec=%java_home%\bin\java)
 
 
:launch
 
 
set COH_OPTS=%COH_OPTS% -Dtangosol.coherence.cacheconfig=\home\oracle\labs\examples-cache-config.xml
 
"%java_exec%" -server -showversion %COH_OPTS% -cp C:\home\oracle\labs;"%coherence_home%\lib\coherence.jar";C:\home\oracle\labs\Security\classes -Xms128m -Xmx128m com.tangosol.net.DefaultCacheServer  %2 %3 %4 %5 %6 %7
 
goto exit
 
:instructions
 
echo Usage:
echo   ^<coherence_home^>\bin\cache-server.cmd
goto exit
 
:exit
endlocal
@echo on

10.2.9 プロキシ・サービスのあるキャッシュ・サーバー用起動ファイルの作成

クラスタ内のキャッシュ・サーバーで拡張プロキシ・サービスを起動するファイルを作成します。Extendクライアントはこのサービスに接続します。起動ファイルには、プロキシ・サービスとクラスタ側のキャッシュ構成ファイルを指定するシステム・プロパティを含める必要があります。クラスパスに、アプリケーション・クラス・ファイルとXML構成ファイルを含める必要もあります。

これらの例で、プロキシ・サービスのあるキャッシュ・サーバーの起動ファイルは、キャッシュ・サーバーの起動ファイルと同じ構成を持ちますが、これには拡張プロキシを有効にするシステム・プロパティが含まれます。

プロキシ・サービスのあるキャッシュ・サーバーの起動ファイルを作成する手順は次のとおりです。

  1. JDeveloperで、起動ファイルを作成します。Securityプロジェクトを右クリックし、「新規」を選択します。「新規ギャラリ」で、「カテゴリ」フィールドの「一般」および「アイテム」フィールドの「ファイル」を選択します。「新規ファイル」ダイアログで、ファイル名としてsecurity-run-proxy.cmdを入力し、これをCoherenceの\binディレクトリ(この場合はC:\oracle\product\coherence\bin)に保存します。

  2. コマンドを入力してキャッシュ・サーバーを起動します。主なコマンドは次のとおりです。

    • プロキシ・サービスを指定するtangosol.coherence.extend.enabledシステム・プロパティ

    • クラスタ側のキャッシュ構成ファイル(この場合はexamples-cache-config.xml)のパスを示すtangosol.coherence.cacheconfigシステム・プロパティ

    • coherence.jarC:\oracle\product\lib\coherence.jar)、Securityクラス(C:\home\oracle\labs\Security\classes)、およびXML構成ファイル(C:\home\oracle\labs)のパスを含めるよう定義されたクラスパス


注意:

クラスパスで、XML構成ファイル(C:\home\oracle\labsの下)はcoherence.jarより前に指定してください。クラス・ローダーは、coherence.jarでカスタムのPOF構成ファイル(この章の後半で作成)を参照する前に、この構成ファイルを見つける必要があります。

例10-9に、プロキシ・サービスのあるクラスタ側キャッシュ・サーバーの起動ファイルsecurity-run-proxy.cmdの可能な実装を示します。

例10-9 プロキシ・サービスのあるキャッシュ・サーバー用起動ファイル

@echo off
@
@REM this will start a proxy cache server
@
setlocal
 
:config
 
@rem specify the Coherence installation directory
set coherence_home=c:\oracle\product\coherence
 
@rem specify the JVM heap size
set memory=512m
 
:start
if not exist "%coherence_home%\lib\coherence.jar" goto instructions
 
if "%java_home%"=="" (set java_exec=java) else (set java_exec=%java_home%\bin\java)
 
 
:launch
 
set COH_OPTS=%COH_OPTS% -Dtangosol.coherence.extend.enabled=true -Dtangosol.coherence.cacheconfig=\home\oracle\labs\examples-cache-config.xml
 
"%JAVA_HOME%"\bin\java -server -showversion %COH_OPTS% -cp C:\home\oracle\labs;"%coherence_home%\lib\coherence.jar";C:\home\oracle\labs\Security\classes -Xms128m -Xmx128m com.tangosol.net.DefaultCacheServer %2 %3 %4 %5 %6 %7
 
goto exit
 
:instructions
 
echo Usage:
echo   ^<coherence_home^>\bin\security-run-proxy.cmd
goto exit
 
:exit
endlocal

10.2.10 プロジェクト・クラスパスおよびランタイム・プロパティの構成

プロジェクトのクラスパスおよびランタイム・プロパティを構成します。

  1. JDeveloperで、プロジェクト内のJavaファイルをコンパイルします。Securityプロジェクトを右クリックし、「Security.jprのメイク」を選択します。

  2. プロジェクトのJavaランタイム・オプションを設定します。

    Securityプロジェクトを右クリックし、「プロジェクト・プロパティ」を選択します。「実行/デバッグ/プロファイル」を選択し、「デフォルト」実行構成を編集します。「Javaオプション」フィールドで、ローカル記憶域を無効にし、クライアント側のキャッシュ構成ファイルclient-cache-config.xmlを指定するよう、システム・プロパティを入力します。

    -Dtangosol.coherence.distributed.localstorage=false -Dtangosol.coherence.cacheconfig=\home\oracle\labs\client-cache-config.xml
    

    OK」をクリックし、「実行構成の編集」ウィンドウを閉じます。

  3. プロジェクトのライブラリおよびクラスパスを設定します。「プロジェクト・プロパティ」ウィンドウで、「ライブラリとクラスパス」をクリックします。coherence.jarSecurityプロジェクトのJavaクラス(C:\home\oracle\labs\Security\classes)、およびXML構成ファイルを含むディレクトリ(C:\home\oracle\labs)をプロジェクト・クラスパスに追加します。


    注意:

    クラスパスで、XML構成ファイル(C:\home\oracle\labsの下)はcoherence.jarより前に指定してください。クラス・ローダーは、coherence.jarでカスタムのPOF構成ファイル(この章の後半で作成)を参照する前に、この構成ファイルを見つける必要があります。

    ライブラリとクラスパス」ウィンドウは図10-1のようになります。

    ライブラリおよびクラスパスの設定の詳細は、「プロジェクト・プロパティの変更によるランタイム構成の設定」を参照してください。

    図10-1 Securityプロジェクトのライブラリおよびクラスパス

    Securityプロジェクトのライブラリおよびクラスパス
  4. OK」をクリックして「ライブラリとクラスパス」ダイアログ・ボックスを閉じ、さらに「OK」をクリックして「プロジェクト・プロパティ」ダイアログ・ボックスを閉じます。

  5. プロジェクトを保存してコンパイルします。プロジェクトを右クリックし、「Security.jprのメイク」を選択します。

10.2.11 パスワード例の実行

パスワード例を実行し、トークンを生成および検証して、プロキシ・サービスに渡します。

  1. Securityプロジェクト内のファイルがまだコンパイルされていない場合は、これらをコンパイルします。

  2. 稼働しているキャッシュ・サーバーがあれば停止します。コマンド・プロンプトを開き、プロキシ・サーバーsecurity-run.proxy.cmdを実行します。

  3. 別のコマンド・プロンプトを開き、キャッシュ・サーバーsecurity-cache-server.cmdを実行します。

  4. PasswordExample.javaを右クリックし、「実行」を選択して、パスワード例を実行します。

JDeveloperログ・ウィンドウに、例10-10のような出力が表示されます。出力には、パスワード例の開始、プロキシ・サーバーへのソケットのオープン、およびこの例の完了が示されます。

例10-10 JDeveloperログ・ウィンドウでのパスワード例の出力

...
------password example begins------
2010-06-09 11:50:49.220/0.344 Oracle Coherence 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded operational configuration from "jar:file:/C:/oracle/product/coherence_3.5/lib/coherence.jar!/tangosol-coherence.xml"
2010-06-09 11:50:49.220/0.344 Oracle Coherence 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded operational overrides from "jar:file:/C:/oracle/product/coherence_3.5/lib/coherence.jar!/tangosol-coherence-override-dev.xml"
2010-06-09 11:50:49.220/0.344 Oracle Coherence 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded operational overrides from "file:/C:/home/oracle/labs/tangosol-coherence-override.xml"
2010-06-09 11:50:49.235/0.359 Oracle Coherence 3.6.0.0 DPR3 <D5> (thread=main, member=n/a): Optional configuration override "/custom-mbeans.xml" is not specified
 
Oracle Coherence Version 3.6.0.0 DPR3 Build 16141
 Grid Edition: Development mode
Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
 
2010-06-09 11:50:49.470/0.594 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded cache configuration from "file:/C:/home/oracle/labs/client-cache-config.xml"
2010-06-09 11:50:49.673/0.797 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=ExtendTcpCacheService:TcpInitiator, member=n/a): Loaded POF configuration from "jar:file:/C:/oracle/product/coherence_3.5/lib/coherence.jar!/pof-config.xml"
2010-06-09 11:50:49.688/0.812 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=ExtendTcpCacheService:TcpInitiator, member=n/a): Loaded included POF configuration from "jar:file:/C:/oracle/product/coherence_3.5/lib/coherence.jar!/coherence-pof-config.xml"
2010-06-09 11:50:49.782/0.906 Oracle Coherence GE 3.6.0.0 DPR3 <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=0, RequestTimeout=0, ConnectTimeout=0, SocketProvider=SystemSocketProvider, RemoteAddresses=[hostname/130.35.99.213:9099], KeepAliveEnabled=true, TcpDelayEnabled=false, ReceiveBufferSize=0, SendBufferSize=0, LingerTimeout=-1}
2010-06-09 11:50:49.798/0.922 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=main, member=n/a): Opening Socket connection to 130.35.99.213:9099
2010-06-09 11:50:49.798/0.922 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Connected to 130.35.99.213:9099
------password example succeeded------
------password example completed------
Process exited with exit code 0.

プロキシ・サーバーのシェルには、例10-11のようなレスポンスが表示されます。ここには、識別名のCNおよびOUの各値と、パスワードが検証されたかどうかが一覧表示されます。

例10-11 プロキシ・サービス・シェルを実行するキャッシュ・サーバーからのレスポンス

...
2010-06-09 11:50:39.829/2.781 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=main, member=2):
Services
  (
  ClusterService{Name=Cluster, State=(SERVICE_STARTED, STATE_JOINED), Id=0, Version=3.6, OldestMemberId=1}
  InvocationService{Name=Management, State=(SERVICE_STARTED), Id=1, Version=3.1, OldestMemberId=6}
  PartitionedCache{Name=PartitionedPofCache, State=(SERVICE_STARTED), LocalStorage=enabled, PartitionCount=257, BackupCount=1, AssignedPartitions=128, BackupPartitions=129}
  ProxyService{Name=ProxyService, State=(SERVICE_STARTED), Id=7, Version=3.2, OldestMemberId=6}
  )

Started DefaultCacheServer...

Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne
Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne
Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne

10.3 クラスタへのロールベース・アクセス制御の組込み

この項では、クラスタへのアクセスにロールベースのポリシーを採用する例を作成する方法について説明します。コードでは、ログインしてユーザーIDが特定のロールに割り当てられているSubjectを取得します。Subjectのコンテキストで実行されているキャッシュ参照を取得し、さらに様々なキャッシュ処理を試みます。ユーザーに付与されたロールにより、キャッシュ処理が許可または拒否されます。この例のロール・マッピングおよびロールベースの認証は簡略化されており、実際のセキュリティ向けではありません。

たとえば、writerロールのユーザーは入力と取得が許可されます。readerロールのユーザーの場合、取得はできますが入力はできません。writerロールのユーザーがキャッシュを破棄することはできません。しかし、adminロールのユーザーはキャッシュを破棄できます。

Subjectのコンテキストでキャッシュ参照が作成されると、そのIDはその参照に永久的に関連付けられます。そのキャッシュ参照はすべて、そのIDのために使用されます。

例では、前項で作成したPasswordIdentityTransformerクラスおよびPasswordIdentityAsserterクラスを使用します。PasswordIdentityTransformerは、パスワード、ユーザーIDおよびロールを含むセキュリティ・トークンを生成します。PasswordIdentityAsserter(プロキシで実行)は、パスワードを適用するセキュリティ・トークンを検証し、適切なユーザーIDおよびロールでSubjectを構築します。セキュリティ・トークンの生成およびアサーションは自動的に行われます。

次の手順に従い、例を作成します。

  1. キャッシュ・メソッドにアクセスする資格を持つユーザー・ロールの定義

  2. キャッシュ・サービスに対する資格の適用

  3. アクセス制御例のプログラムの作成

  4. クラスタ側のキャッシュ構成ファイルの編集

  5. アクセス制御例の実行

10.3.1 キャッシュ・メソッドにアクセスする資格を持つユーザー・ロールの定義

ユーザーのロールに基づいてキャッシュ・メソッドへのアクセスを許可する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);
        }

キャッシュ・メソッドをコールできるユーザーを指定するファイルを作成する手順は次のとおりです。

  1. SecurityプロジェクトにEntitledNamedCacheという新規Javaクラスを作成します。

    詳細は、「Javaクラスの作成」を参照してください。

  2. クラスがWrapperNamedCacheをインポートおよび拡張することを確認します。

  3. FilterMapListenerValueExtractorCollectionComparatorMap、およびSetの各クラスをインポートします。WrapperNamedCacheEntitledNamedCacheも含む)内のメソッドは、これらのタイプで引数を使用します。

  4. 特定のロールのユーザーのみがメソッドをコールできるように、EntitledNamedCacheにメソッドを実装します。

例10-12に、EntitledNamedCacheの可能な実装を示します。

例10-12 資格のある名前付きキャッシュ

package com.oracle.handson;
 
 
import com.tangosol.net.NamedCache;
 
import com.tangosol.net.cache.WrapperNamedCache;
 
 
import com.tangosol.util.Filter;
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;
 
 
/**
* 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(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(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(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);
        }
 
 
    // ----- helper methods -------------------------------------------------
 
    /**
    * Return the wrapped NamedCache.
    *
    * @return  the wrapped CacheService
    */
    public NamedCache getNamedCache()
        {
        return (NamedCache) getMap();
        }
    }

10.3.2 キャッシュ・サービスに対する資格の適用

Coherence*Extendによりクライアントから渡されたSubjectを使用して、ラップされたCacheServiceに対するアクセス権限を適用する方法を示すファイルを作成します。この実装では、前項で作成したEntitledNamedCacheに、キャッシュ処理のアクセス制御を委任する必要があります。

作成するクラスは、com.tangosol.net.WrapperCacheServiceを拡張する必要があります。このクラスは、CacheService上のメソッドを保護できる便利な機能です。プロキシのキャッシュ・サービスとクライアント・リクエスト間で委任を行うメカニズムも提供します。

特定ロールのユーザーのみが使用できるように、メソッドensureCachereleaseCacheおよび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);
        }

キャッシュ・サービスにアクセスする資格を適用するファイルを作成する手順は次のとおりです。

  1. SecurityプロジェクトにEntitledCacheServiceという新規Javaクラスを作成します。

  2. クラスがWrapperCacheServiceをインポートおよび拡張することを確認します。

  3. 特定ロールのユーザーのみが使用できるように、ensureCachereleaseCacheおよびdestroyCacheの各メソッドを実装します。

例10-13に、EntitledCacheServiceの可能な実装を示します。

例10-13 資格のあるキャッシュ・サービス

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);
        }
    }

10.3.3 アクセス制御例のプログラムの作成

アクセス制御例を実行するファイルを作成します。ロール・ポリシーは、SecurityExampleHelperに定義されます。EntitledCacheServiceファイルおよびEntitledNamedCacheファイルでポリシーが適用されます。

プログラムは、SecurityHelperFile.loginへの引数として様々なユーザーを指定し、さらにキャッシュでの読取りや書込み、および処理の破棄を試みます。処理は、EntitledCacheServiceおよびEntitledNamedCacheで定義された資格ポリシーに基づいて成功または失敗します。

  1. Securityプロジェクトで、mainメソッドを含むAccessControlExampleという名前の新規Javaクラスを作成します。

    詳細は、「Javaクラスの作成」を参照してください。

  2. キャッシュにアクセスするmainを実装します。

  3. loginメソッドに対する引数として、SecurityExampleHelperファイルで定義したユーザーを指定します。

  4. ユーザーごとに、キャッシュ上で読取り(get)、書込み(put)およびdestroy処理を試み、それに応じて成功または失敗のメッセージを表示します。

例10-14に、AccessControlExample.javaの可能な実装を示します。

例10-14 アクセス制御例を実行するサンプル・プログラム

package com.oracle.handson;
 
 
import com.tangosol.net.CacheFactory;
import com.tangosol.net.InvocationService;
import com.tangosol.net.NamedCache;
import java.security.PrivilegedExceptionAction;
 
import java.util.Map;
 
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------");
        } 
    }

10.3.4 クラスタ側のキャッシュ構成ファイルの編集

クラスタ側のキャッシュ構成ファイルexamples-cache-config.xmlを編集します。proxy-configの下のcache-service-proxy行に、キャッシュ・サービスのクラス名のフルパスを指定します。cache-service-proxy行には、プロキシ・サービスによって管理されるキャッシュ・サービス・プロキシの構成情報を記述します。

この場合、キャッシュ・サービス・クラス名はcom.oracle.handson.EntitledCacheServiceプロキシで、param-typecom.tangosol.net.CacheServiceとします。

例10-15に、構成に追加するXMLコードを示します。

例10-15 クラスタ側のキャッシュ構成のキャッシュ・サービス・プロキシ構成

...
      <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>
...

10.3.5 アクセス制御例の実行

アクセス制御例を実行し、ユーザーのロールに基づいてキャッシュへのアクセスを付与または拒否する方法を示します。

  1. Securityプロジェクト内のファイルがまだコンパイルされていない場合は、これらをコンパイルします。

  2. 稼働しているキャッシュ・サーバーがあれば停止します。security-run-proxy.cmdにより、プロキシ・サービスを実行するキャッシュ・サーバーを起動します。

  3. security-cache-server.cmdにより、クラスタ内のキャッシュ・サーバーを起動します。

  4. AccessControlExample.javaファイルを右クリックし、「実行」を選択します。

JDeveloperログ・ウィンドウに、例10-16のような出力が表示されます。メッセージは、キャッシュで読取り、書込み、および破棄の処理を試みる、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-16 JDeveloperログ・ウィンドウでのアクセス制御例の出力

...
------cache access control example begins------
2010-06-10 17:07:51.500/0.344 Oracle Coherence 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded operational configuration from "jar:file:/C:/oracle/product0529/coherence_3.5/lib/coherence.jar!/tangosol-coherence.xml"
2010-06-10 17:07:51.500/0.344 Oracle Coherence 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded operational overrides from "jar:file:/C:/oracle/product0529/coherence_3.5/lib/coherence.jar!/tangosol-coherence-override-dev.xml"
2010-06-10 17:07:51.500/0.344 Oracle Coherence 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded operational overrides from "file:/C:/home/oracle/labs/tangosol-coherence-override.xml"
2010-06-10 17:07:51.500/0.344 Oracle Coherence 3.6.0.0 DPR3 <D5> (thread=main, member=n/a): Optional configuration override "/custom-mbeans.xml" is not specified
 
Oracle Coherence Version 3.6.0.0 DPR3 Build 16141
 Grid Edition: Development mode
Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
 
2010-06-10 17:07:51.703/0.547 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded cache configuration from "file:/C:/home/oracle/labs/client-cache-config.xml"
2010-06-10 17:07:51.906/0.750 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=ExtendTcpCacheService:TcpInitiator, member=n/a): Loaded POF configuration from "jar:file:/C:/oracle/product0529/coherence_3.5/lib/coherence.jar!/pof-config.xml"
2010-06-10 17:07:51.906/0.750 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=ExtendTcpCacheService:TcpInitiator, member=n/a): Loaded included POF configuration from "jar:file:/C:/oracle/product0529/coherence_3.5/lib/coherence.jar!/coherence-pof-config.xml"
2010-06-10 17:07:52.015/0.859 Oracle Coherence GE 3.6.0.0 DPR3 <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=0, RequestTimeout=0, ConnectTimeout=0, SocketProvider=SystemSocketProvider, RemoteAddresses=[tpfaeffl-lap7/130.35.99.213:9099], KeepAliveEnabled=true, TcpDelayEnabled=false, ReceiveBufferSize=0, SendBufferSize=0, LingerTimeout=-1}
2010-06-10 17:07:52.031/0.875 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=main, member=n/a): Opening Socket connection to 130.35.99.213:9099
2010-06-10 17:07:52.031/0.875 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Connected to 130.35.99.213:9099
    Success: read and write allowed
2010-06-10 17:07:52.125/0.969 Oracle Coherence GE 3.6.0.0 DPR3 <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=0, RequestTimeout=0, ConnectTimeout=0, SocketProvider=SystemSocketProvider, RemoteAddresses=[tpfaeffl-lap7/130.35.99.213:9099], KeepAliveEnabled=true, TcpDelayEnabled=false, ReceiveBufferSize=0, SendBufferSize=0, LingerTimeout=-1}
2010-06-10 17:07:52.125/0.969 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=main, member=n/a): Opening Socket connection to 130.35.99.213:9099
2010-06-10 17:07:52.125/0.969 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Connected to 130.35.99.213:9099
    Success: read allowed
    Success: Correctly cannot write
    Success: Correctly cannot destroy the cache
2010-06-10 17:07:52.187/1.031 Oracle Coherence GE 3.6.0.0 DPR3 <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=0, RequestTimeout=0, ConnectTimeout=0, SocketProvider=SystemSocketProvider, RemoteAddresses=[tpfaeffl-lap7/130.35.99.213:9099], KeepAliveEnabled=true, TcpDelayEnabled=false, ReceiveBufferSize=0, SendBufferSize=0, LingerTimeout=-1}
2010-06-10 17:07:52.187/1.031 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=main, member=n/a): Opening Socket connection to 130.35.99.213:9099
2010-06-10 17:07:52.187/1.031 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Connected to 130.35.99.213:9099
    Success: Correctly allowed to destroy the cache
------cache access control example completed------
Process exited with exit code 0.

例10-17に、キャッシュ・サーバーがプロキシ・サービスを実行しているシェルの出力を示します。出力内のセキュリティの例外は、JDeveloperログ・ウィンドウのSuccess: Correctly cannot writeメッセージおよびSuccess: Correctly cannot destroy the cacheメッセージに対応します。

例10-17 プロキシ・サービスを実行するキャッシュ・サーバーの出力

2010-06-10 17:07:25.468/18.156 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=Cluster, member=1): Member 2 joined Service ProxyService with senior member 1
Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne
Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne
Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne
Password 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
2010-06-10 17:07:52.140/44.828 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=Proxy:ProxyService:TcpAcceptorWorker:0, 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:91)
        at com.oracle.handson.EntitledNamedCache.put(EntitledNamedCache.java:66)
        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)
 
2010-06-10 17:07:52.140/44.828 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=Proxy:ProxyService:TcpAcceptorWorker:1, 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:91)
        at com.oracle.handson.EntitledCacheService.destroyCache(EntitledCacheService.java:64)
        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

10.4 起動可能なオブジェクトへのロールベース・アクセス制御の組込み

起動サービス・クラスタ・サービスにより、Extendクライアントはクラスタ上の起動可能なオブジェクトを実行できます。この例は、起動可能なオブジェクトを実行できるユーザーを決定するロールベースのポリシーを使用する方法を示します。

たとえば、writerロールのユーザーは起動可能なオブジェクトを実行できます。readerロールのユーザーは実行できません。

この例では、クライアント・プログラムからコールできる簡単な起動可能オブジェクトを作成します。これはシリアライズできるため、POF構成ファイルにリストすることも必要です。ロールに基づいてユーザーがサービス上でメソッドを実行できるかどうかをテストする起動サービス・プログラムも作成します。

前述の例と同様に、この例ではPasswordIdentityTransformerを使用して、パスワード、ユーザーIDおよびロールを含むセキュリティ・トークンを生成します。PasswordIdentityAsserter(プロキシで実行)を使用して、パスワードを適用するセキュリティ・トークンを検証し、適切なユーザーIDおよびロールでSubjectを構築します。セキュリティ・トークンの生成およびアサーションは自動的に行われます。

次の手順に従い、例を作成します。

  1. 起動可能なオブジェクトの作成

  2. 資格のある起動サービスの作成

  3. 起動サービスへのアクセス例のプログラムの作成

  4. クラスタ側のキャッシュ構成ファイルの編集

  5. POF構成ファイルの作成

  6. 起動サービスへのアクセス例の実行

10.4.1 起動可能なオブジェクトの作成

資格のある起動サービスにより使用される簡単な起動可能オブジェクトの実装を作成します。たとえば、起動可能なオブジェクトは、増分して整数を返すように記述できます。

起動可能なオブジェクトを作成する手順は次のとおりです。

  1. SecurityプロジェクトにExampleInvocableという新規Javaクラスを作成します。

    詳細は、「Javaクラスの作成」を参照してください。

  2. InvocableおよびInvocationServiceの各インタフェースをインポートします。このクラスはシリアライズ可能なオブジェクトを扱うため、PortableObjectPofReaderおよびPofWriterもインポートします。

  3. ExampleInvocableクラスがInvocableおよびPortableObjectを実装することを確認します。

  4. 整数を増分して結果を返すExampleInvocableを実装します。

  5. PofReader.readExternalメソッドおよびPofWriter.writeExternalメソッドを実装します。

例10-18に、ExampleInvocable.javaの可能な実装を示します。

例10-18 起動可能なオブジェクトのサンプル

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;
    }

10.4.2 資格のある起動サービスの作成

この例は、リモートの起動サービスをラップしてアクセス制御を行う方法を示します。Coherence*Extendを使用してクライアントから渡されたSubjectを使用し、ラップされたInvocationServiceへのアクセス権限を適用できます。この実装では、特定ロールのクライアントのみがラップされた起動サービスにアクセスできます。

作成するクラスは、com.tangosol.net.WrapperInvocationServiceクラスを拡張する必要があります。このクラスは、InvocationService上のメソッドを保護できる便利な機能です。プロキシの起動サービスとクライアント・リクエスト間で委任を行うメカニズムも提供します。

資格のある起動サービスを作成する手順は次のとおりです。

  1. SecurityプロジェクトにEntitledInvocationServiceという新規Javaクラスを作成します。

  2. InvocableInvocationObserverInvocationServiceWrapperInvocationServiceMap、およびSetの各インタフェースをインポートします。EntitledInvocationServiceクラスがWrapperInvocationServiceを拡張していることを確認します。

  3. queryメソッドおよびexecuteメソッドを実装します。この実装に、特定のユーザー・ロール(この場合はROLE_WRITER)がこれらの処理にアクセスできるかどうかを決定するSecurityExampleHelper.checkAccessのコールを含めます。

例10-19に、EntitledInvocationService.javaの可能な実装を示します。

例10-19 資格のある起動サービスのサンプル

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);
        }
    }

10.4.3 起動サービスへのアクセス例のプログラムの作成

起動サービスへのアクセス例を実行するプログラムを作成します。プログラムの目的は、SecurityExampleHelperで定義された様々なユーザーが起動可能なオブジェクトにアクセスし、これを実行することができるかどうかをテストすることです。ロールベースのポリシーの適用は、EntitledInvocationServiceで指定します。

起動サービスへのアクセス例を実行するプログラムを作成する手順は次のとおりです。

  1. Securityプロジェクト内に、mainメソッドを含むAccessInvocationServiceExample.javaというJavaクラスを作成します。

    詳細は、「Javaクラスの作成」を参照してください。

  2. その他のクラスの中から、ExampleInvocableCacheFactoryおよびInvocationServiceをインポートします。

  3. accessInvocationServiceメソッドを起動するmainメソッドを実装します。

  4. accessInvocationServiceを実装し、SecurityExampleHelperで定義された様々なユーザーが、サービスへのログインおよびExampleInvocableで定義されたオブジェクトの実行を試みるようにします。SecurityExampleHelper.loginメソッドを使用し、様々なユーザーが起動可能なサービスにアクセスできるかどうかをテストします。

例10-20に、AccessInvocationServiceExample.javaの可能な実装を示します。

例10-20 起動サービスへのアクセス例を実行するサンプル・プログラム

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------");
        }
    }

10.4.4 クラスタ側のキャッシュ構成ファイルの編集

examples-cache-config.xmlファイルを編集し、proxy-configの下のinvocation-service-proxy行に起動サービスのフルパスを追加します。invocation-service-proxy行には、プロキシ・サービスによって管理される起動サービス・プロキシの構成情報を記述します。

この場合、起動サービス・クラス名はcom.oracle.handson.EntitledInvocationServiceで、param-typecom.tangosol.net.InvocationServiceとします。

例10-21 クラスタ側キャッシュの起動サービス・プロキシ構成

   ...
      <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>
   ...

10.4.5 POF構成ファイルの作成

C:\home\oracle\labsディレクトリにsecurity-pof-config.xmlというPOF構成ファイルを作成します。ここでユーザー定義型としてExampleInvocableが宣言されます。

例10-22 ExampleInvocableユーザー定義型のPOF構成ファイル

<?xml version="1.0"?>
 
<!DOCTYPE pof-config SYSTEM "pof-config.dtd">
 
<pof-config>
  <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>

10.4.6 起動サービスへのアクセス例の実行

起動サービスへのアクセス例を実行し、ユーザーのロールに基づいて起動可能なオブジェクトへのアクセスを付与または拒否する方法を示します。

  1. 稼働しているキャッシュ・サーバーがあれば停止します。security-run-proxy.cmdにより、プロキシ・サービスを実行するキャッシュ・サーバーを起動します。

  2. security-cache-server.cmdにより、クラスタ内のキャッシュ・サーバーを起動します。

  3. JDeveloperで、Securityプロジェクトがまだコンパイルされていない場合は、これをコンパイルします。

  4. AccessInvocationServiceExample.javaファイルを右クリックし、「実行」を選択します。

JDeveloperログ・ウィンドウに、例10-23のような出力が表示されます。メッセージは、起動可能なオブジェクトExampleInvocableの実行を試みる、AccessInvocationServiceExampleに指定されたユーザーに対応します。

  • Success: Correctly allowed to use the invocation serviceメッセージ: ExampleInvocableの実行を試みる、ロールwriterを持つユーザーに対応します。

  • Success: Correctly unable to use the invocation serviceメッセージ: ExampleInvocableの実行を試みる、ロールreaderを持つユーザーに対応します。

例10-23 JDeveloperログ・ウィンドウ

C:\oracle\product0529\jdk160_18\bin\javaw.exe -client -classpath C:\home\oracle\labs\.adf;C:\home\oracle\labs\Security\classes;C:\home\oracle\labs;C:\oracle\product0529\coherence_3.5\lib\coherence.jar -Dweblogic.webservice.client.proxyusername=tom.pfaeffle -Dweblogic.webservice.client.proxypassword=p3rtha -Djavax.net.ssl.trustStore=C:\oracle\product\wlserver_10.3\server\lib\DemoTrust.jks -Dhttp.proxyHost=www-proxy.us.oracle.com -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts=*.local|*oraclecorp.com|*oracle.com|localhost|localhost.localdomain|127.0.0.1|::1|tpfaeffl-lap7.us.oracle.com|tpfaeffl-lap7 -Dhttps.proxyHost=www-proxy.us.oracle.com -Dhttps.proxyPort=80 -Dhttps.nonProxyHosts=*.local|*oraclecorp.com|*oracle.com|localhost|localhost.localdomain|127.0.0.1|::1|tpfaeffl-lap7.us.oracle.com|tpfaeffl-lap7 -Dtangosol.coherence.distributed.localstorage=false -Dtangosol.coherence.cacheconfig=\home\oracle\labs\client-cache-config.xml com.oracle.handson.AccessInvocationServiceExample
------InvocationService access control example begins------
2010-06-17 12:17:04.362/0.391 Oracle Coherence 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded operational configuration from "jar:file:/C:/oracle/product0529/coherence_3.5/lib/coherence.jar!/tangosol-coherence.xml"
2010-06-17 12:17:04.362/0.391 Oracle Coherence 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded operational overrides from "jar:file:/C:/oracle/product0529/coherence_3.5/lib/coherence.jar!/tangosol-coherence-override-dev.xml"
2010-06-17 12:17:04.362/0.391 Oracle Coherence 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded operational overrides from "file:/C:/home/oracle/labs/tangosol-coherence-override.xml"
2010-06-17 12:17:04.362/0.391 Oracle Coherence 3.6.0.0 DPR3 <D5> (thread=main, member=n/a): Optional configuration override "/custom-mbeans.xml" is not specified
 
Oracle Coherence Version 3.6.0.0 DPR3 Build 16141
 Grid Edition: Development mode
Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
 
2010-06-17 12:17:04.580/0.609 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded cache configuration from "file:/C:/home/oracle/labs/client-cache-config.xml"
2010-06-17 12:17:04.799/0.828 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=ExtendTcpInvocationService:TcpInitiator, member=n/a): Loaded POF configuration from "file:/C:/home/oracle/labs/security-pof-config.xml"
2010-06-17 12:17:04.799/0.828 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=ExtendTcpInvocationService:TcpInitiator, member=n/a): Loaded included POF configuration from "jar:file:/C:/oracle/product0529/coherence_3.5/lib/coherence.jar!/coherence-pof-config.xml"
2010-06-17 12:17:04.877/0.906 Oracle Coherence GE 3.6.0.0 DPR3 <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=[tpfaeffl-lap7/130.35.99.213:9099], KeepAliveEnabled=true, TcpDelayEnabled=false, ReceiveBufferSize=0, SendBufferSize=0, LingerTimeout=-1}
2010-06-17 12:17:04.877/0.906 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=main, member=n/a): Opening Socket connection to 130.35.99.213:9099
2010-06-17 12:17:04.893/0.922 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Connected to 130.35.99.213:9099
    Success: Correctly allowed to use the invocation service
2010-06-17 12:17:04.955/0.984 Oracle Coherence GE 3.6.0.0 DPR3 <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=[tpfaeffl-lap7/130.35.99.213:9099], KeepAliveEnabled=true, TcpDelayEnabled=false, ReceiveBufferSize=0, SendBufferSize=0, LingerTimeout=-1}
2010-06-17 12:17:04.955/0.984 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=main, member=n/a): Opening Socket connection to 130.35.99.213:9099
2010-06-17 12:17:04.955/0.984 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Connected to 130.35.99.213:9099
    Success: Correctly unable to use the invocation service
------InvocationService access control example completed------
Process exited with exit code 0.

例10-24に、キャッシュ・サーバーがプロキシ・サービスを実行しているシェルの出力を示します。出力内のセキュリティの例外は、JDeveloperログ・ウィンドウのSuccess: Correctly unable to use the invocation serviceメッセージ(この場合、readerロールのユーザーがExampleInvocableの実行を試みます)に対応します。

例10-24 プロキシ・サービス・ウィンドウ

...
Started DefaultCacheServer...
 
Password validated for user: role_writer
Password validated for user: role_writer
Password validated for user: role_reader
Password validated for user: role_reader
2010-06-17 12:17:04.971/41.250 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=Proxy:ProxyService:TcpAcceptorWorker:1, member=3): 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:91)
        at com.oracle.handson.EntitledInvocationService.query(EntitledInvocationService.java:51)
        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)