362 lines
15 KiB
HTML
362 lines
15 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 LDAP 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 and the Apache DS LDAP server.</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 in the Apache DS LDAP server, how to configure topics with
|
|
proper permissions using wild-card expressions, and how they take effects in a simple program.</p>
|
|
|
|
<p>Users and roles are configured in Apache DS. The SecurityExample class will start an embedded version of Apache
|
|
DS and load the contents of example.ldif which contains the users and passwords for this example.</p>
|
|
|
|
<pre class="prettyprint">
|
|
<code>
|
|
dn: dc=activemq,dc=org
|
|
dc: activemq
|
|
objectClass: top
|
|
objectClass: domain
|
|
|
|
dn: uid=bill,dc=activemq,dc=org
|
|
uid: bill
|
|
userPassword: activemq
|
|
objectClass: account
|
|
objectClass: simpleSecurityObject
|
|
objectClass: top
|
|
|
|
dn: uid=andrew,dc=activemq,dc=org
|
|
uid: andrew
|
|
userPassword: activemq1
|
|
objectClass: account
|
|
objectClass: simpleSecurityObject
|
|
objectClass: top
|
|
|
|
dn: uid=frank,dc=activemq,dc=org
|
|
uid: frank
|
|
userPassword: activemq2
|
|
objectClass: account
|
|
objectClass: simpleSecurityObject
|
|
objectClass: top
|
|
|
|
dn: uid=sam,dc=activemq,dc=org
|
|
uid: sam
|
|
userPassword: activemq3
|
|
objectClass: account
|
|
objectClass: simpleSecurityObject
|
|
objectClass: top
|
|
|
|
###################
|
|
## Define roles ##
|
|
###################
|
|
|
|
dn: cn=user,dc=activemq,dc=org
|
|
cn: user
|
|
member: uid=bill,dc=activemq,dc=org
|
|
member: uid=andrew,dc=activemq,dc=org
|
|
member: uid=frank,dc=activemq,dc=org
|
|
member: uid=sam,dc=activemq,dc=org
|
|
objectClass: groupOfNames
|
|
objectClass: top
|
|
|
|
dn: cn=europe-user,dc=activemq,dc=org
|
|
cn: europe-user
|
|
member: uid=andrew,dc=activemq,dc=org
|
|
objectClass: groupOfNames
|
|
objectClass: top
|
|
|
|
dn: cn=news-user,dc=activemq,dc=org
|
|
cn: news-user
|
|
member: uid=frank,dc=activemq,dc=org
|
|
member: uid=sam,dc=activemq,dc=org
|
|
objectClass: groupOfNames
|
|
objectClass: top
|
|
|
|
dn: cn=us-user,dc=activemq,dc=org
|
|
cn: us-user
|
|
member: uid=frank,dc=activemq,dc=org
|
|
objectClass: groupOfNames
|
|
objectClass: top
|
|
</code>
|
|
</pre>
|
|
|
|
<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>
|
|
|
|
<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>
|