activemq-artemis/examples/jms/security
jbertram e6a3d3a0c5 ACTIVEMQ6-14 Replace JNDI server with client impl 2014-12-05 09:27:52 -06:00
..
src/main ACTIVEMQ6-14 Replace JNDI server with client impl 2014-12-05 09:27:52 -06:00
pom.xml ACTIVEMQ6-14 Replace JNDI server with client impl 2014-12-05 09:27:52 -06:00
readme.html Remove references of hornetq 2014-11-20 13:00:37 +00:00

readme.html

<html>
  <head>
    <title>ActiveMQ JMS Security Example</title>
    <link rel="stylesheet" type="text/css" href="../common/common.css" />
    <link rel="stylesheet" type="text/css" href="../common/prettify.css" />
    <script type="text/javascript" src="../common/prettify.js"></script>
  </head>
  <body onload="prettyPrint()">
     <h1>JMS Security Example</h1>

     <p>This example shows how to configure and use security using ActiveMQ.</p>
     
     <p>With security properly configured, ActiveMQ can restrict client access to its resources, including 
     connection creation, message sending/receiving, etc. This is done by configuring users and roles as well as permissions in 
     the configuration files. </p>

     <p>ActiveMQ supports wild-card security configuration. This feature makes security configuration very
     flexible and enables fine-grained control over permissions in an efficient way.</p>
     
     <p>For a full description of how to configure security with ActiveMQ, please consult the user
     manual.</p>
     
     <p>This example demonstrates how to configure users/roles, how to configure topics with proper permissions using wild-card
     expressions, and how they take effects in a simple program. </p>
     
     <p>First we need to configure users with roles. Users and Roles are configured in <code>activemq-users.xml</code>. This example has four users
     configured as below </p>
     
     <pre class="prettyprint">
     <code>
		   &lt;user name=&quot;bill&quot; password=&quot;activemq&quot;&gt;
		      &lt;role name=&quot;user&quot;/&gt;
		   &lt;/user&gt;
		  
		   &lt;user name=&quot;andrew&quot; password=&quot;activemq1&quot;&gt;
		      &lt;role name=&quot;europe-user&quot;/&gt;
		      &lt;role name=&quot;user&quot;/&gt;
		   &lt;/user&gt;
		   
		   &lt;user name=&quot;frank&quot; password=&quot;activemq2&quot;&gt;
		      &lt;role name=&quot;us-user&quot;/&gt;
		      &lt;role name=&quot;news-user&quot;/&gt;
		      &lt;role name=&quot;user&quot;/&gt;
		   &lt;/user&gt;
		   
		   &lt;user name=&quot;sam&quot; password=&quot;activemq3&quot;&gt;
		      &lt;role name=&quot;news-user&quot;/&gt;
		      &lt;role name=&quot;user&quot;/&gt;
		   &lt;/user&gt;
     </code>
     </pre>
     
     <p>
     Each user has three properties available: user name, password, and roles it belongs to. It should be noted that
     a user can belong to more than one role. In the above configuration, all users belong to role 'user'. User 'andrew' also
     belongs to role 'europe-user', user 'frank' also belongs to 'us-user' and 'news-user' and user 'sam' also belongs to 'news-user'.
     </p>
     <p>
     User name and password consists of a valid account that can be used to establish connections to a ActiveMQ server, while 
     roles are used in controlling the access privileges against ActiveMQ topics and queues. You can achieve this control by
     configuring proper permissions in <code>activemq-configuration.xml</code>, like the following
     </p>
     <pre class="prettyprint"><code>
      &lt;security-settings&gt;
         &lt;!-- any user can have full control of generic topics --&gt;
		   &lt;security-setting match=&quot;jms.topic.#&quot;&gt;
		      &lt;permission type=&quot;createDurableQueue&quot; roles=&quot;user&quot;/&gt;
		      &lt;permission type=&quot;deleteDurableQueue&quot; roles=&quot;user&quot;/&gt;
		      &lt;permission type=&quot;createNonDurableQueue&quot; roles=&quot;user&quot;/&gt;
		      &lt;permission type=&quot;deleteNonDurableQueue&quot; roles=&quot;user&quot;/&gt;
		      &lt;permission type=&quot;send&quot; roles=&quot;user&quot;/&gt;
		      &lt;permission type=&quot;consume&quot; roles=&quot;user&quot;/&gt;
		   &lt;/security-setting&gt;
		
		   &lt;security-setting match=&quot;jms.topic.news.europe.#&quot;&gt;
		      &lt;permission type=&quot;createDurableQueue&quot; roles=&quot;user&quot;/&gt;
		      &lt;permission type=&quot;deleteDurableQueue&quot; roles=&quot;user&quot;/&gt;
		      &lt;permission type=&quot;createNonDurableQueue&quot; roles=&quot;user&quot;/&gt;
		      &lt;permission type=&quot;deleteNonDurableQueue&quot; roles=&quot;user&quot;/&gt;
		      &lt;permission type=&quot;send&quot; roles=&quot;europe-user&quot;/&gt;
		      &lt;permission type=&quot;consume&quot; roles=&quot;news-user&quot;/&gt;
		   &lt;/security-setting&gt;
		
		   &lt;security-setting match=&quot;jms.topic.news.us.#&quot;&gt;
		      &lt;permission type=&quot;createDurableQueue&quot; roles=&quot;user&quot;/&gt;
		      &lt;permission type=&quot;deleteDurableQueue&quot; roles=&quot;user&quot;/&gt;
		      &lt;permission type=&quot;createNonDurableQueue&quot; roles=&quot;user&quot;/&gt;
		      &lt;permission type=&quot;deleteNonDurableQueue&quot; roles=&quot;user&quot;/&gt;
		      &lt;permission type=&quot;send&quot; roles=&quot;us-user&quot;/&gt;
		      &lt;permission type=&quot;consume&quot; roles=&quot;news-user&quot;/&gt;
		   &lt;/security-setting&gt;
     &lt;/security-settings&gt;
     </code></pre>
     
     <p>Permissions can be defined on any group of queues, by using a wildcard. You can easily specify 
     wildcards to apply certain permissions to a set of matching queues and topics. In the above configuration
     we have created four sets of permissions, each set matches against a special group of targets, indicated by wild-card match attributes.</p>
     
     <p>You can provide a very broad permission control as a default and then add more strict control
     over specific addresses. By the above we define the following access rules:</p>
     
         <li>Only role 'us-user' can create/delete and pulish messages to topics whose names match wild-card pattern 'news.us.#'.</li>
         <li>Only role 'europe-user' can create/delete and publish messages to topics whose names match wild-card pattern 'news.europe.#'.</li>
         <li>Only role 'news-user' can subscribe messages to topics whose names match wild-card pattern 'news.us.#' and 'news.europe.#'.</li>
         <li>For any other topics that don't match any of the above wild-card patterns, permissions are granted to users of role 'user'.</li>
         
     <p>To illustrate the effect of permissions, three topics are deployed. Topic 'genericTopic' matches 'jms.topic.#' wild-card, topic 'news.europe.europeTopic' matches 
     jms.topic.news.europe.#' wild-cards, and topic 'news.us.usTopic' matches 'jms.topic.news.us.#'.</p>
     
     <p>With ActiveMQ, the security manager is also configurable. You can use JAASSecurityManager or JBossASSecurityManager based on you need. Please
     check out the activemq-beans.xml for how to do. In this example we just use the basic ActiveMQSecurityManagerImpl which reads users/roles/passwords from the xml
     file <code>activemq-users.xml</code>.


     <h2>Example step-by-step</h2>
     <p><i>To run the example, simply type <code>mvn verify</code> from this directory</i></p>

     <ol>
        <li>First we need to get an initial context so we can look-up the JMS connection factory and destination objects from JNDI. This initial context will get it's properties from the <code>client-jndi.properties</code> file in the directory <code>../common/config</code></li>
        <pre class="prettyprint">
           <code>
           InitialContext initialContext = getContext(0);
           </code>
        </pre>

        <li>We perform lookup on the topics</li>
        <pre class="prettyprint">
           <code>
           Topic genericTopic = (Topic) initialContext.lookup("/topic/genericTopic");
           Topic europeTopic = (Topic) initialContext.lookup("/topic/europeTopic");
           Topic usTopic = (Topic) initialContext.lookup("/topic/usTopic");
           </code>
        </pre>

        <li>We perform a lookup on the Connection Factory</li>
        <pre class="prettyprint">
           <code>
           ConnectionFactory cf = (ConnectionFactory) initialContext.lookup("/ConnectionFactory");
           </code>
        </pre>

        <li>We try to create a JMS Connection without user/password. It will fail.</li>
        <pre class="prettyprint">
           <code>
           try
           {
              cf.createConnection();
              result = false;
           }
           catch (JMSSecurityException e)
           {
              System.out.println("Default user cannot get a connection. Details: " + e.getMessage());
           }
           </code>
        </pre>

        <li>Bill tries to make a connection using wrong password</li>
        <pre class="prettyprint">
           <code>
           billConnection = null;
           try
           {
              billConnection = createConnection("bill", "activemq1", cf);
              result = false;
           }
           catch (JMSException e)
           {
              System.out.println("User bill failed to connect. Details: " + e.getMessage());
           }
           </code>
        </pre>

        <li>Bill makes a good connection.</li>
        <pre class="prettyprint">
          <code>
           billConnection = createConnection("bill", "activemq", cf);
           billConnection.start();
          </code>
       </pre>

        <li>Andrew makes a good connection</li>
         <pre class="prettyprint">
           <code>
           andrewConnection = createConnection("andrew", "activemq1", cf);
           andrewConnection.start();
           </code>
         </pre>

        <li>Frank makes a good connection</li>
        <pre class="prettyprint">
           <code>
           frankConnection = createConnection("frank", "activemq2", cf);
           frankConnection.start();
           </code>
        </pre>

        <li>Sam makes a good connection</li>
        <pre class="prettyprint">
           <code>
           samConnection = createConnection("sam", "activemq3", cf);
           samConnection.start();
           </code>
        </pre>

        <li>We check every user can publish/subscribe genericTopics</li>
        <pre class="prettyprint">
           <code>
           checkUserSendAndReceive(genericTopic, billConnection, "bill");
           checkUserSendAndReceive(genericTopic, andrewConnection, "andrew");
           checkUserSendAndReceive(genericTopic, frankConnection, "frank");
           checkUserSendAndReceive(genericTopic, samConnection, "sam");
           </code>
        </pre>

        <li>We check permissions on news.europe.europeTopic for bill: can't send and can't receive</li>
        <pre class="prettyprint">
           <code>
           checkUserNoSendNoReceive(europeTopic, billConnection, "bill", andrewConnection, frankConnection);
           </code>
        </pre>

        <li>We check permissions on news.europe.europeTopic for andrew: can send but can't receive</li>
        <pre class="prettyprint">
           <code>
           checkUserSendNoReceive(europeTopic, andrewConnection, "andrew", frankConnection);
           </code>
        </pre>

        <li>We check permissions on news.europe.europeTopic for frank: can't send but can receive</li>
        <pre class="prettyprint">
           <code>
           checkUserReceiveNoSend(europeTopic, frankConnection, "frank", andrewConnection);
           </code>
        </pre>

        <li>We check permissions on news.europe.europeTopic for sam: can't send but can receive</li>
        <pre class="prettyprint">
           <code>
           checkUserReceiveNoSend(europeTopic, samConnection, "sam", andrewConnection);
           </code>
        </pre>

        <li>We check permissions on news.us.usTopic for bill: can't send and can't receive</li>
        <pre class="prettyprint">
           <code>
           checkUserNoSendNoReceive(usTopic, billConnection, "bill");
           </code>
        </pre>

        <li>We check permissions on news.us.usTopic for andrew: can't send and can't receive</li>
        <pre class="prettyprint">
           <code>
           checkUserNoSendNoReceive(usTopic, andrewConnection, "andrew");
           </code>
        </pre>

        <li>We check permissions on news.us.usTopic for frank: can both send and receive</li>
        <pre class="prettyprint">
           <code>
           checkUserSendAndReceive(usTopic, frankConnection, "frank");
           </code>
        </pre>

        <li>We check permissions on news.us.usTopic for sam: can't send but can receive</li>
        <pre class="prettyprint">
           <code>
           checkUserReceiveNoSend(usTopic, samConnection, "sam", frankConnection);
           </code>
        </pre>
        
        <li>And finally, <b>always</b> remember to close your JMS connections and resources after use, in a <code>finally</code> block. Closing a JMS connection will automatically close all of its sessions, consumers, producer and browser objects</li>

        <pre class="prettyprint">
           <code>
           finally
           {
              if (billConnection != null)
              {
                 billConnection.close();
              }
              if (andrewConnection != null)
              {
                 andrewConnection.close();
              }
              if (frankConnection != null)
              {
                 frankConnection.close();
              }
              if (samConnection != null)
              {
                 samConnection.close();
              }
		         
              // Also the initialContext
              if (initialContext != null)
              {
                 initialContext.close();
              }
           }
           </code>
        </pre>
     </ol>
  </body>
</html>