ActiveMQ 使用mysql实现连接授权
activemq默认提供通过配置文件来管理用户连接凭证,少量几个用户那无所谓,若是几百上千的客户端都要连接,且不能使用相同的用户名和密码,那配置文件就不好管理了。Activemq提供了BrokerPlugin接口,可以实现接口的installPlugin方法,该方法需要返回一个BrokerFilter的实例。BrokerFilter是一个过滤器,可以根据实际情况重写addConnection方法,如果用户信息连接信息被认证,则调用父类addConnection方法,允许客户端连接,否则拒绝。mysql的操作是使用springframework的jdbcTemplate工具类,通过spring注入的方式,在activemq启动的时候注入datasource对象,然后通过BrokerPlugin实现类的构造方法接收jdbcTemplate对象,然后再传给BrokerFilter实例。不用手写连接mysql的代码。
BrokerPlugin的实现代码
package com.lijietao.plugin.activemq;
import org.apache.activemq.broker.Broker;
import org.apache.activemq.broker.BrokerPlugin;
import org.springframework.jdbc.core.JdbcTemplate;
public class userAuthplugin implements BrokerPlugin{
JdbcTemplate jdbcTemplate;
public userAuthplugin(JdbcTemplate jdbcTemplate){
this.jdbcTemplate = jdbcTemplate;
}
public Broker installPlugin(Broker broker) throws Exception{
return new userAuth(broker,jdbcTemplate);
}
}
BrokerFilter重写方代码
package com.lijietao.plugin.activemq;
import org.apache.activemq.broker.Broker;
import org.apache.activemq.broker.BrokerFilter;
import org.apache.activemq.broker.ConnectionContext;
import org.apache.activemq.command.ConnectionInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import com.lijietao.plugin.activemq.bean.mqttUser;
public class userAuth extends BrokerFilter {
private JdbcTemplate jdbcTemplate;
private static Log log = LogFactory.getLog(userAuth.class);
public userAuth(Broker next, JdbcTemplate jdbcTemplate){
super(next);
this.jdbcTemplate = jdbcTemplate;
}
@Override
public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {
log.info("**********connection auth check @ " + info.getUserName());
if (auth(info.getUserName(),info.getPassword())){
log.info("**********auth check pass @ " + info.getUserName());
super.addConnection(context,info);
}else{
log.error("**********auth check fail @ " + info.getUserName() + " @ password " + info.getPassword());
}
}
private boolean auth(String userName,String passWord){
mqttUser user = getUser(userName,passWord);
log.info("******USER Object @ " + user.toString());
return !user.getUserName().equals("");
}
private mqttUser getUser(String userName,String passWord){
String sql = "SELECT * FROM tb_mqtt_user WHERE username=? AND password=?";
mqttUser user = jdbcTemplate.queryForObject(sql,new Object[]{userName,passWord},new BeanPropertyRowMapper<mqttUser>(mqttUser.class));
return user;
}
}
数据库配置文件db.properties,部署的时候放到activemq的conf目录下。
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/test?autoReconnect=true&useUnicode=true&characterEncoding=utf8
jdbc.username=lijietao
jdbc.password=lijietao
activemq.xml配置文件修改
1.spring注入配置,在beans标签内增加bean注入配置。
<!-- 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>
<!-- 增加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>
2.在broker节点增加plugin标签
<!-- 鉴权插件 -->
<plugins>
<bean xmlns="http://www.springframework.org/schema/beans" id="userAuthplugin" class="com.lijietao.plugin.activemq.userAuthplugin">
<constructor-arg>
<ref bean="jdbcTemplate"/>
</constructor-arg>
</bean>
</plugins>
3.properties配置文件
<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>
最后部署,将插件项目打包成jar文件,放到lib目录下。同时也需要把mysql-connector-java的jar包放到lib目录下。
因为项目中用到了springframework的jdbctemplate,所以还要把spring-jdbc依赖包放到lib/optional目录下。mysql的表结构很简单,两个字段,username和password。
再次重启activemq服务,启动mqtt客户端连接,可以发现,如果mqtt连接的用户名在mysql里面查不到,则会连接失败。
上面的过程还可以再扩展,比如后台要收集所有的消息记录,则可以重写send方法,将消息重新分发一份到另一个destination。