327 lines
14 KiB
HTML
327 lines
14 KiB
HTML
<!--
|
|
Licensed to the Apache Software Foundation (ASF) under one
|
|
or more contributor license agreements. See the NOTICE file
|
|
distributed with this work for additional information
|
|
regarding copyright ownership. The ASF licenses this file
|
|
to you under the Apache License, Version 2.0 (the
|
|
"License"); you may not use this file except in compliance
|
|
with the License. You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing,
|
|
software distributed under the License is distributed on an
|
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
KIND, either express or implied. See the License for the
|
|
specific language governing permissions and limitations
|
|
under the License.
|
|
-->
|
|
|
|
<html>
|
|
<head>
|
|
<title>ActiveMQ Artemis 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>
|
|
|
|
<pre>To run the example, simply type <b>mvn verify</b> from this directory, <br>or <b>mvn -PnoServer verify</b> if you want to start and create the server manually.</pre>
|
|
|
|
|
|
<p>This example shows how to configure and use security using ActiveMQ Artemis.</p>
|
|
|
|
<p>With security properly configured, ActiveMQ Artemis 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 Artemis 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 Artemis, 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 Artemis server, while
|
|
roles are used in controlling the access privileges against ActiveMQ Artemis topics and queues. You can achieve this control by
|
|
configuring proper permissions in <code>broker.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 Artemis, 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 -Pexample</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>
|