SQL Authentication for ActiveMQ
Learn how to write your own security plugin for ActiveMQ.
Join the DZone community and get the full member experience.
Join For FreeToday, we are going to look into securing an ActiveMQ broker with Java and SQL by writing our own plugin for ActiveMQ.
You can also refer to the ActiveMQ official documentation that explains how you can write your custom plugins for ActiveMQ here and here.
We will create a simple application in Java that will intercept all clients that try to connect to our broker, and based on the clientID, we will search for the username and password for the client trying to connect to our broker. If the username and password match the pre-configured username and password against the ClientID, we will let the client connect to our broker, or else, we will throw a security exception.
The basic concept behind what we are developing is as follows:
- The flexibility of ActiveMQ plugin API comes from the
BrokerFilter
class - The
BrokerFilter
class provides the ability to intercept many of the available broker-level operations - You have to extend the
BrokerFilter
class and override its methods according to your needs. (We are going to override theaddConnection
method that is triggered when a client is trying to connect to the broker). - You have to implement the
BrokerPlugin
class and override itsinstallPlugin
method in order to instantiate the plugin and return a new intercepted broker.
That means we have to create two classes in total:
1) A class responsible for the installation of the plugin.
2) A class that will contain the authentication logic.
Let's get our hands dirty with the coding part.
Create a new java project (preferably a Maven project) and add the dependencies for the active-mq broker and SQL connector:
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-broker</artifactId>
<version>5.15.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
We will create the Installer
class as an implementation of the BrokerPlugin
class, like below
package com.authplugin.deviceauth;
import java.sql.Connection;
import java.sql.DriverManager;
import org.apache.activemq.broker.Broker;
import org.apache.activemq.broker.BrokerPlugin;
public class MyAuthPlugin implements BrokerPlugin {
public String url;
public String username;
public String password;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Broker installPlugin(Broker broker) throws Exception {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection con = DriverManager.getConnection(this.url, this.username, this.password);
return new MyAuthBroker(broker, con);
}
}
Here, we have provided the necessary properties for our plugin to work.
As the plugin will be initialized in a similar way as a Spring Bean in XML, we can specify the values for the properties like data source URL, username, and password in the XML file where we are going to place our plugin.
Next, we have to create the filter class by extending BrokerFilter,
and as we are going to filter the connections to our broker, we are going to override the addConnection
method, as shown below:
package com.authplugin.deviceauth;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.activemq.broker.Broker;
import org.apache.activemq.broker.BrokerFilter;
import org.apache.activemq.broker.ConnectionContext;
import org.apache.activemq.command.ConnectionInfo;
public class MyAuthBroker extends BrokerFilter {
PreparedStatement pstmt;
public MyAuthBroker(Broker next,Connection con) {
super(next);
try {
pstmt = con.prepareStatement("select * from devices where clientID= ?");
} catch (SQLException e) {
e.printStackTrace();
}
}
public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {
try {
pstmt.setString(1, info.getClientId());
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
String clientID = rs.getString("clientID");
String username = rs.getString("username");
String password = rs.getString("password");
if (username.equals(info.getUserName()) && password.equals(info.getPassword())) {
super.addConnection(context, info);
} else {
// username or password does not match
throw new SecurityException("invalid username or password from deviceID : " + clientID);
}
} else {
// device id not in DB, throw exception
throw new SecurityException("Client ID not in DB : " + info.getClientId());
}
} catch (SecurityException e) {
System.out.println(e.getMessage());
throw new Exception();
}
}
}
After this, we need to create a table of devicesin the database that can store the username and password for the various clients (devices in my case) with at least three columns — clientID, username, and password.
Compile this project to produce a jar that we would place in the broker (deviceauth-0.0.1-SNAPSHOT.jar)
Now, we just need to place the plugin in the lib folder of the ActiveMQ broker installation directory along with the SQL Connector jar. Remember to place the SQL Connector in the lib folder as well.
After placing the plugin as well as the connector jars in the lib folder, go to the
{activemq-installation-directory}/conf/activemq.xml and put the following lines in between the broker tag with proper database connection properties. Also, if you have named the class something other than the one provided in the bean tag below, you have to update that too:
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}">
.
.
.
<plugins>
<bean id="authPlugin" class="com.authplugin.deviceauth.MyAuthPlugin" xmlns="http://www.springframework.org/schema/beans">
<property name="url">
<value>{your-db-URL-here}</value>
</property>
<property name="username">
<value>{your-db-username-here}</value>
</property>
<property name="password">
<value>{your-db-password-here}</value>
</property>
</bean>
</plugins>
.
</broker>
That's it! Start the broker, and now, only clients with username and password assigned to them in the database, according to the clientID, can connect to the broker.
The full code for the plugin is available on my GitHub here.
I hope this post was helpful and that you learned something new today. Thank you for reading and happy coding!
Opinions expressed by DZone contributors are their own.
Trending
-
Operator Overloading in Java
-
Reactive Programming
-
DevOps Midwest: A Community Event Full of DevSecOps Best Practices
-
What Is Istio Service Mesh?
Comments