mirror of
https://github.com/apache/activemq-artemis.git
synced 2025-02-08 02:59:14 +00:00
<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>