ActiveMQ custom authentication (database link) + JAAS authorization

ActiveMQ supports pluggable security mechanisms to switch between different provider s. Authorization authentication supports three ways:

1.simpleAuthenticationPlugin

Just add the following configuration to the \ conf \ ActiveMQ. XML

<plugins>
   <simpleAuthenticationPlugin>
     <users>
       <authenticationUser username="system" password="manager" groups="users,admins"/>
     </users>
   </simpleAuthenticationPlugin>
</plugins>

The plugins tag should be placed in the broker tag

Add authorized user system password manager the user group is users and admins

2. Use JAAS certification

 \conf\login.config

activemq-domain { 
    org.apache.activemq.jaas.PropertiesLoginModule required debug=true
    org.apache.activemq.jaas.properties.user="users.properties"
    org.apache.activemq.jaas.properties.group="groups.properties"; 
}; 

debug=true: when an exception occurs in the authentication process, detailed exception information can be output;

reload=true: refresh memory configuration information after adding users (groups) or changing users (groups) and passwords (no need to restart activemq service)

users.properties user configuration

groups.properties user rights group configuration

plugins>

<jaasAuthenticationPlugin configuration="activemq-domain" />

<authorizationPlugin>
					<map>
						<authorizationMap>
							<authorizationEntries>
								<authorizationEntry queue=">" read="admins" write="admins" admin="admins" />
								<authorizationEntry queue="USERS.>" read="users" write="users" admin="users" />
								<authorizationEntry queue="GUEST.>" read="guests" write="guests,users" admin="guests,users" />
								<authorizationEntry topic=">" read="admins" write="admins" admin="admins" />
								<authorizationEntry topic="test.>" read="test" write="test" admin="test" />
								<authorizationEntry topic="GUEST.>" read="guests" write="guests,users" admin="guests,users" />
								<authorizationEntry topic="ActiveMQ.Advisory.>" read="guests,users,test" write="guests,users,test" admin="guests,users,test"/>
							</authorizationEntries>
						</authorizationMap>
					</map>
				</authorizationPlugin>

</plugins>

Authorization configuration

  • read: you can receive messages from the queue or topic
  • write: can send message to queue or topic
  • admin: you can create a queue or topic (there may be other functions)

Note here that queue topic represents the name of point-to-point or pub/sub

The wildcards > or * can be used to represent queue and topic

3. User defined authentication method

Build maven project import dependency

 <!-- activemq drive -->
  	<dependency>
    	<groupId>org.apache.activemq</groupId>
    	<artifactId>activemq-broker</artifactId>
    	<version>5.15.12</version>
	</dependency>
	
	<dependency>
    	<groupId>org.apache.activemq</groupId>
    	<artifactId>activemq-jaas</artifactId>
   	 	<version>5.15.12</version>
	</dependency>
   	<!-- mysql drive -->
     <dependency>
 		<groupId>mysql</groupId>
 		<artifactId>mysql-connector-java</artifactId>
 		<version>8.0.18</version>
     </dependency>
     <dependency>
    	<groupId>org.springframework</groupId>
		<artifactId>spring-jdbc</artifactId>
		  <version>4.3.26.RELEASE</version>
    </dependency>
        
	  <!-- logs -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.7</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.1.7</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-access</artifactId>
            <version>1.1.7</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.1.7</version>
        </dependency>

Custom broker authentication

package com.kedalo.borker.plugin;

import org.apache.activemq.broker.Broker;
import org.apache.activemq.broker.BrokerPlugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;

import com.kedalo.auth.plugin.LoginAuthPlugin;



/**
 * @author lyl
 *
 * @desc Custom broker
 * 
 * 2020 11:37:00 am, April 16, 2016
 */
public class AuthBroker implements BrokerPlugin {
	final Logger log = LoggerFactory.getLogger(AuthBroker.class);
	JdbcTemplate jdbcTemplate;
	
	public AuthBroker(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}
	
	
	

	public Broker installPlugin(Broker arg0) throws Exception {
		log.info("===============    Initialize authentication ============================");
		return new LoginAuthPlugin(arg0,jdbcTemplate);
	}

}

Authentication components here

package com.kedalo.auth.plugin;

import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.HashSet;
import java.util.Set;

import org.apache.activemq.broker.Broker;
import org.apache.activemq.broker.ConnectionContext;
import org.apache.activemq.command.ConnectionInfo;
import org.apache.activemq.jaas.GroupPrincipal;
import org.apache.activemq.security.AbstractAuthenticationBroker;
import org.apache.activemq.security.SecurityContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;

import com.kedalo.auth.db.DBMapper;
import com.kedalo.auth.model.User;


/**
 * @author lyl
 *
 * @desc Authentication component
 * 
 * 2020 11:37:18 am, April 16, 2016
 */
public class LoginAuthPlugin extends AbstractAuthenticationBroker {

	private JdbcTemplate jdbcTemplate;	
	final Logger log = LoggerFactory.getLogger(LoginAuthPlugin.class);
	private DBMapper db = null;
	
	public LoginAuthPlugin(Broker next,JdbcTemplate jdbcTemplate) {
		super(next);
		this.jdbcTemplate = jdbcTemplate;
		db = DBMapper.getInstance(jdbcTemplate);
	}

	@Override
	public SecurityContext authenticate(String username, String password, X509Certificate[] arg2) throws SecurityException {
		SecurityContext securityContext = null;
        User user = db.login(username, password);
        log.info("username:{}  password:{}",username,password);
        //Verify user information
        if (user == null) {
        	 throw new SecurityException("Wrong user name or password!");
        } 
        log.info("group:{}",user.getGroup());
        securityContext = new SecurityContext(username) {
            @Override
            public Set<Principal> getPrincipals() {
                Set<Principal> groups = new HashSet<Principal>();
                groups.add(new GroupPrincipal(user.getGroup()));
                return groups;
            }
        };
        
        return securityContext;
	}

	  /**
     * Block when creating a connection
     *
     * @param context
     * @param info
     * @throws Exception
     */
    @Override
    public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {
        SecurityContext securityContext = context.getSecurityContext();
        if (securityContext == null) {
            securityContext = authenticate(info.getUserName(), info.getPassword(), null);
            context.setSecurityContext(securityContext);
            securityContexts.add(securityContext);
        }
        try {
            super.addConnection(context, info);
        } catch (Exception e) {
            securityContexts.remove(securityContext);
            context.setSecurityContext(null);
            e.printStackTrace();
            throw new Exception("Insufficient user authority!");
        }
    }

}

Mapper

package com.kedalo.auth.db;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import com.kedalo.auth.model.User;
import com.kedalo.auth.util.StringUtil;


/**
 * @author lyl
 *
 * @desc query
 * 
 * 2020 11:54:08 am, April 16, 2016
 */
public class DBMapper {
	private static DBMapper INSTANCE;
	
	JdbcTemplate jdbcTemplate;
	
	
	final Logger log = LoggerFactory.getLogger(DBMapper.class);
	
	
	public DBMapper(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}
	
	public static DBMapper getInstance(JdbcTemplate jdbcTemplate) {
		if (INSTANCE == null)
			synchronized(DBMapper.class){
				INSTANCE = new DBMapper(jdbcTemplate);
			}
		return INSTANCE;
	}
	
	
	public User login(String username,String password) {
		String sql = "select a.*,ug.`group` from kedalo_user a " + 
				" LEFT JOIN kedalo_user_group ug on a.user_id = ug.user_id  where username= ? and password= ?";
        try {
        	System.out.println(jdbcTemplate == null);
            User user = jdbcTemplate.queryForObject(sql, new Object[]{username,StringUtil.md5(password)}, new BeanPropertyRowMapper<User>(User.class));
            return user;
        } catch (EmptyResultDataAccessException e) {
        	log.error("Login exception:"+e);
            return null;
        }
	}
	
	
	
	
	
	
	
}

I will create two tables here, one user table and one grouping table

 

Custom authentication code here

Next, we need to configure our authentication in activeMQ.xml

	<plugins> 
				
				
				 <authorizationPlugin>
					<map>
						<authorizationMap>
							<authorizationEntries>
								<authorizationEntry queue=">" read="admins" write="admins" admin="admins" />
								<authorizationEntry queue="USERS.>" read="users" write="users" admin="users" />
								<authorizationEntry queue="GUEST.>" read="guests" write="guests,users" admin="guests,users" />
								<authorizationEntry topic=">" read="admins" write="admins" admin="admins" />
								<authorizationEntry topic="test.>" read="test" write="test" admin="test" />
								<authorizationEntry topic="GUEST.>" read="guests" write="guests,users" admin="guests,users" />
								<authorizationEntry topic="ActiveMQ.Advisory.>" read="guests,users,test" write="guests,users,test" admin="guests,users,test"/>
							</authorizationEntries>
						</authorizationMap>
					</map>
				</authorizationPlugin>
				<bean xmlns="http://www.springframework.org/schema/beans" id="myPlugin" class="com.kedalo.borker.plugin.AuthBroker">						
					<constructor-arg>
						<ref bean="jdbcTemplate"/>
					</constructor-arg>
				</bean>
				
				
			</plugins> 

The plugins tag should be placed in the broker tag

	<!-- mysql Database data source-->  
    <bean id="mySqlDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">  
            <property name="driverClassName" value="${jdbc.driverClassName}" />  
            <property name="url" value="${jdbc.url}" />  
            <property name="username" value="${jdbc.username}" />  
            <property name="password" value="${jdbc.password}" />  
    </bean>

JDBC

	<!-- increase jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" abstract="false"
        lazy-init="false" autowire="default" >
        <property name="dataSource">
            <ref bean="mySqlDataSource" />
        </property>
    </bean>

Our configuration file db.properties

  <!-- Allows us to use system properties as variables in this configuration file -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
			<list>
				<value>file:${activemq.conf}/credentials.properties</value>
				<value>file:${activemq.conf}/db.properties</value>
			</list>
        </property>
    </bean>

After activeMQ configuration, we need to put our jar package

mysql-connector-java-8.0.18.jar is placed in the activeMQ/lib / folder
spring-jdbc-4.3.26.RELEASE.jar is placed in the activeMQ/lib/ optional / folder

slf4j-api-1.7.7.jar logback-core-1.1.7.jar logback-access-1.1.7.jar logback-classic-1.1.7.jar is placed in the activeMQ/lib / folder

Start activeMq

 

Startup success

Let's test and write a link DEMO

package activemqClient.activemqClient;

import java.io.IOException;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;

import org.apache.activemq.ActiveMQConnectionFactory;

/**
 * Hello world!
 *
 */
public class Sub {
    public static void main( String[] args ) throws JMSException, IOException {
    	 //Create connection factory
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("root","1234563","tcp://127.0.0.1:61616");
        //Create connection
        Connection connection = connectionFactory.createConnection();
        //start-up
        connection.start();
        //Creating the session object false indicates whether to submit automatically;
        Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
        //Create subclass of queue des!
        Topic topic = session.createTopic("qtest.f");
        //Create consumer
        MessageConsumer messageConsumer = session.createConsumer(topic);
        messageConsumer.setMessageListener(new MessageListener() {
            public void onMessage(Message message) {
                TextMessage textMessage = (TextMessage) message;
                try { 
                    System.out.println("PUB:"+textMessage.getText());
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        });
        System.in.read();
        //close resource
        messageConsumer.close();
        session.close();
        connection.close();
    }
}

Here, I write the wrong password on purpose. Look back

Then subscribe to topic s without permission

 

About the key points of authority

<authorizationEntry topic="ActiveMQ.Advisory.>" read="guests,users,test" write="guests,users,test" admin="guests,users,test"/>

This must be configured. As soon as the client establishes a connection, the first request is tcp://ActiveMQ.Advisory. Therefore, authentication + JAAS needs to set all packets

Tags: Programming Apache JDBC Session Java

Posted on Thu, 16 Apr 2020 03:54:03 -0700 by macewan