<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>
<user name="bill" password="activemq">
<role name="user"/>
</user>
<user name="andrew" password="activemq1">
<role name="europe-user"/>
<role name="user"/>
</user>
<user name="frank" password="activemq2">
<role name="us-user"/>
<role name="news-user"/>
<role name="user"/>
</user>
<user name="sam" password="activemq3">
<role name="news-user"/>
<role name="user"/>
</user>
</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>
<security-settings>
<!-- any user can have full control of generic topics -->
<security-setting match="jms.topic.#">
<permission type="createDurableQueue" roles="user"/>
<permission type="deleteDurableQueue" roles="user"/>
<permission type="createNonDurableQueue" roles="user"/>
<permission type="deleteNonDurableQueue" roles="user"/>
<permission type="send" roles="user"/>
<permission type="consume" roles="user"/>
</security-setting>
<security-setting match="jms.topic.news.europe.#">
<permission type="createDurableQueue" roles="user"/>
<permission type="deleteDurableQueue" roles="user"/>
<permission type="createNonDurableQueue" roles="user"/>
<permission type="deleteNonDurableQueue" roles="user"/>
<permission type="send" roles="europe-user"/>
<permission type="consume" roles="news-user"/>
</security-setting>
<security-setting match="jms.topic.news.us.#">
<permission type="createDurableQueue" roles="user"/>
<permission type="deleteDurableQueue" roles="user"/>
<permission type="createNonDurableQueue" roles="user"/>
<permission type="deleteNonDurableQueue" roles="user"/>
<permission type="send" roles="us-user"/>
<permission type="consume" roles="news-user"/>
</security-setting>
</security-settings>
</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>