By default, ActiveMQ does not request a Username or password for consumers and producers. That means, everyone who knows your broker URL is able to connect to your broker and read your messages and/or send messages. For personal development this isn’t a big issue, but for business applications security plays an important role.
Because of that, I have played around with the authentication of consumers and producers on ActiveMQ brokers.
First of all you should know, that ActiveMQ supports a users and groups based authentication.In our example we would like to have two groups:
- senders: they are allowed to send messages into queues (write access)
- receivers: they are allowed to consume the messages (read access)
Setup groups and users for Receiver and Consumer:
To setup those two groups and one user for each group you have to add these lines (starting with „plugins“) to your activemq.xml :
ATTENTION: in earlier versions (I tested it also with 5.5.1) you have to insert the plugins-element in the correct alphabetical order. See also: http://activemq.apache.org/xml-reference.html –> Alphabetically ordered xml elements (5.4 – 5.5.1)
<!-- The <broker> element is used to configure the ActiveMQ broker. --> <broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}"> <plugins> <simpleAuthenticationPlugin> <users> <authenticationUser username="superman" password="boss" groups="senders,receivers,admins" /> <authenticationUser username="mySendingApp" password="manager" groups="senders" /> <authenticationUser username="myReceivingApp" password="manager" groups="receivers" /> </users> </simpleAuthenticationPlugin> </plugins>
Link groups and users to queues and topics
After that you need to grant rights to the created groups „senders“ , „receivers“ and „admins“. Add the following lines after the closing </simpleAuthenticationPlugin> tag:
<authorizationPlugin> <map> <authorizationMap> <authorizationEntries> <authorizationEntry queue=">" write="senders" read="receivers" admin="admins" /> <authorizationEntry topic="ActiveMQ.Advisory.>" write="senders" read="receivers" admin="admins,senders,receivers" /> </authorizationEntries> </authorizationMap> </map> </authorizationPlugin>
Explanation: You can specify different access rights for the single queues. In my example I allow all users from group „senders“ to write inside all queues. queue=“>“ stands for all queues. ActiveMQ uses the „>“ as Wildcard (see also the website-link below). Also I allow all receivers to read from the queue.
Maybe you are wondering about the second <authorizationEntry> . ActiveMQ-Clients creating Advisory-Topics for several reasons. For each queue a client connects to, the client tries to create a Advisory-Topic. That is the reason why I added the second line. This line defines that all clients (with correct password and username) are able to create Topics that are named „ActiveMQ.Advisory.*“. More about ActiveMQs advisory stuff see website link below.
Now you can restart the ActiveMQ broker in order to read the new configuration.
Client side: connect to a username/password protected queue
Now we have to setup the client side to connect to a secured broker. The ConnectionFactory class provides a method called „createConnection“. There is an overloaded method of this: createConnection(String arg1 , String arg2) where arg1 is the username and arg2 is the password.
Here a short example for a listener connecting to a secured broker:
public void startListener() throws NamingException, JMSException, InterruptedException { // Create a JNDI API InitialContext object Properties props = new Properties(); props.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.activemq.jndi.ActiveMQInitialContextFactory"); props.setProperty(Context.PROVIDER_URL, "tcp://" + server + ":61616"); jndiContext = new InitialContext(props); connectionFactory = (ConnectionFactory) jndiContext.lookup("ConnectionFactory"); destination = (Destination) jndiContext.lookup("dynamicQueues/" + queueName); // Connect to ActiveMQ connection = connectionFactory.createConnection(username, password); connection.setClientID(clientID); // this helps to ensure, that not 2 instances can connect to the broker simultaneously // because it is not allowed to connect to the same broker with the same clientID connection.start(); session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageConsumer subscriber = session.createConsumer(destination); subscriber.setMessageListener(new MessageListener() { @Override public void onMessage(Message msg) { try { System.out.println("Message arrived by consumer-ID : " + clientID + " CONTENT = " + ((TextMessage) msg).getText()); } catch (JMSException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); for (int i = 0; i < 100; i++) { System.out.println("still alive .... " + i); Thread.sleep(10 * 1000); } }
Have fun with simple secured JMS 😉
——————————————————————
Sources / Websites:
ActiveMQ Wildcards: http://activemq.apache.org/wildcards.html
ActiveMQ Sample Borker Config for secured JMS Connections: http://activemq.apache.org/complex-single-broker-configuration-stomp-only.html
ActiveMQ Advisory Topics: http://activemq.apache.org/advisory-message.html
Java API for ConnectionFactory: http://docs.oracle.com/javaee/1.4/api/javax/jms/ConnectionFactory.html