ActiveMQ 自定义认证(数据库链接)+JAAS授权

て烟熏妆下的殇ゞ 提交于 2020-04-16 21:35:57

【推荐阅读】微服务还能火多久?>>>

ActiveMQ支持可插拔的安全机制,用以在不同的provider之间切换 ,授权认证支持三种方式:

1.simpleAuthenticationPlugin

只需要在\conf\ActiveMQ .xml 中加入下列配置就可

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

plugins 标签需放在broker标签里面

上述配置的意思添加授权用户 system 密码manager 该用户分组为users 和admins

2.使用JAAS 认证 

 \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 : 在认证过程发生异常,可以输出详细的异常信息;

reload=true : 在增加用户(组)或变更用户(组)及密码后,刷新内存配置信息(不需要重启 activemq 服务)

users.properties 用户配置

groups.properties 用户权限组配置

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>

授权配置 

  • read:可以从queue或者topic里面接收消息
  • write:可以向queue或者topic发送消息
  • admin:可以创建queue或者topic(可能还有别的功能)

这里需要注意的点是 queue topic 代表点对点 或者 pub/sub端的名称 

 queue和topic  可使用通配符 > 或者*来表示

3.自定义认证方式

建立maven 项目导入依赖

 <!-- activemq 驱动 -->
  	<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 驱动 -->
     <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>

自定义broker认证

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 自定义broker
 * 
 * 2020年4月16日上午11:37:00
 */
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("===============    初始化认证 ============================");
		return new LoginAuthPlugin(arg0,jdbcTemplate);
	}

}

这里认证组件

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 认证组件
 * 
 * 2020年4月16日上午11:37:18
 */
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);
        //验证用户信息
        if (user == null) {
        	 throw new SecurityException("用户名或者密码错误!");
        } 
        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;
	}

	  /**
     * 创建连接的时候拦截
     *
     * @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("用户权限不足!");
        }
    }

}

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 查询
 * 
 * 2020年4月16日上午11:54:08
 */
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("登录出现异常:"+e);
            return null;
        }
	}
	
	
	
	
	
	
	
}

我这里建立2 个表一个user表一个分组表

 

 自定义认证代码到此

接下来需要在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> 

plugins 标签需放在broker标签里面

	<!-- mysql数据库数据源-->  
    <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

	<!-- 增加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>

我们的配置文件 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>

activeMQ配置完毕接下来需要放我们的jar包

mysql-connector-java-8.0.18.jar 放置在activeMQ/lib/ 文件夹中
spring-jdbc-4.3.26.RELEASE.jar  放置在activeMQ/lib/ optional/文件夹中

slf4j-api-1.7.7.jar logback-core-1.1.7.jar logback-access-1.1.7.jar logback-classic-1.1.7.jar 放置在activeMQ/lib/ 文件夹中

启动activeMq

 

启动成功

我们来测试一下写一个链接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 {
    	 //创建连接工厂
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("root","1234563","tcp://127.0.0.1:61616");
        //创建连接
        Connection connection = connectionFactory.createConnection();
        //启动
        connection.start();
        //创建session对象 false表示是否自动提交;
        Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
        //创建队列 des的子类!
        Topic topic = session.createTopic("qtest.f");
        //创建消费者
        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();
        //关闭资源
        messageConsumer.close();
        session.close();
        connection.close();
    }
}

这里我故意写错密码看看返回

然后在订阅没有权限的topic

 

关于权限这款重点中的重点

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

这个一定要配置上 因为客户端一建立连接首先的请求就是 tcp://ActiveMQ.Advisory 所以认证+JAAS 这块需要设置所有的分组

 

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!