Activemq 通过开发自定义插件连接 MySql, 登录时查询数据库用户信息实现登录认证授权
自定义插件开发分为六步:
1. 创建 UserAuthPlugin 类实现 BrokerPlugin 接口, 重写 installPlugin(Broker broker) 方法:
package com.xy.auth;
import org.apache.activemq.broker.Broker;
import org.apache.activemq.broker.BrokerPlugin;
import java.util.List;
public class UserAuthPlugin implements BrokerPlugin {
private static final String TAG = " ------ UserAuthPlugin ";
List<String> configs;
public Broker installPlugin(Broker broker) throws Exception {
System.out.println(TAG + "installPlugin(Broker broker)");
GoAuth.INSTANCE.AuthPluginInit("init", 2, "relases v2");
return new MqttAuthorizationBroker(broker, configs);
}
public List<String> getConfigs() {
return configs;
}
public void setConfigs(List<String> configs) {
this.configs = configs;
}
}
2. 创建 MqttAuthorizationBroker 类,继承 BrokerFilter , 重写 addConnection(ConnectionContext context, ConnectionInfo info) 方法中实现登录认证, 在 addConsumer 和 addProducer 方法中实现可读和可写授权
package com.xy.auth;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.apache.activemq.broker.Broker;
import org.apache.activemq.broker.BrokerFilter;
import org.apache.activemq.broker.Connection;
import org.apache.activemq.broker.ConnectionContext;
import org.apache.activemq.broker.ProducerBrokerExchange;
import org.apache.activemq.broker.region.CompositeDestinationInterceptor;
import org.apache.activemq.broker.region.Destination;
import org.apache.activemq.broker.region.DestinationInterceptor;
import org.apache.activemq.broker.region.RegionBroker;
import org.apache.activemq.broker.region.Subscription;
import org.apache.activemq.command.ActiveMQDestination;
import org.apache.activemq.command.BrokerInfo;
import org.apache.activemq.command.ConnectionInfo;
import org.apache.activemq.command.ConsumerInfo;
import org.apache.activemq.command.DestinationInfo;
import org.apache.activemq.command.Message;
import org.apache.activemq.command.ProducerInfo;
import org.apache.activemq.security.SecurityContext;
/**
* 认证授权接口实现
*
* @author Administrator
*
*/
public class MqttAuthorizationBroker extends BrokerFilter {
private static final String TAG = " ------ MqttAuthorizationBroker ";
List<String> configs;
public MqttAuthorizationBroker(Broker next, List<String> configs) {
super(next);
this.configs = configs;
final RegionBroker regionBroker = (RegionBroker) next.getAdaptor(RegionBroker.class);
final CompositeDestinationInterceptor compositeInterceptor = (CompositeDestinationInterceptor) regionBroker
.getDestinationInterceptor();
DestinationInterceptor[] interceptors = compositeInterceptor.getInterceptors();
interceptors = Arrays.copyOf(interceptors, interceptors.length + 1);
interceptors[interceptors.length - 1] = new MqttAuthorizationDestinationInterceptor(this);
compositeInterceptor.setInterceptors(interceptors);
}
/**
* 用户名密码认证
*
* @param clientId
* @param userName
* @param passWord
* @return
*/
private boolean isValid(String clientId, String userName, String passWord) {
int isValid = GoAuth.INSTANCE.AuthUnpwdCheck(userName, passWord, clientId);
return 1 == isValid;
}
/**
* 授权
*
* @param destination
* @param clientid
* @param username
* @param topic
* @param acc 1: subscribe, 2: publish, 3: pubsub
* @return
*/
protected boolean checkDestinationAdmin(ActiveMQDestination destination, String clientid, String username,
String topic, int acc) {
System.out.println(TAG + "checkDestinationAdmin clientid: " + clientid + " username: " + username + " topic: "
+ topic + " acc: " + acc);
int isAuth = GoAuth.INSTANCE.AuthAclCheck(clientid, username, topic, acc);
return 1 == isAuth;
}
@Override
public void addBroker(Connection connection, BrokerInfo info) {
System.out.println(TAG + "addBroker(Connection connection, BrokerInfo info)");
super.addBroker(connection, info);
}
@Override
public void brokerServiceStarted() {
System.out.println(TAG + "brokerServiceStarted()");
super.brokerServiceStarted();
}
/**
* 由于验证需要在创建连接时进行,因此重写BrokerFilter的addConnection方法
*/
@Override
public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {
System.out.println(TAG + "addConnection(ConnectionContext context, ConnectionInfo info)");
if (isValid(info.getClientId(), info.getUserName(), info.getPassword())) {
super.addConnection(context, info);
} else {
System.out
.println(TAG + "is Auth fail username: " + info.getUserName() + " password: " + info.getPassword());
throw new SecurityException("Connection faild username or password error");
}
}
/**
* //由于需要授权是否可读消息,因此重写BrokerFilter的 addConsumer
* 方法,在该方法中,从访问控制列表中查看是否具有读授权,并调用next引用的addConsumer方法
*/
@Override
public Subscription addConsumer(ConnectionContext context, ConsumerInfo info) throws Exception {
System.out.println(TAG + "addConsumer(ConnectionContext context, ConsumerInfo info)");
ActiveMQDestination destination = info.getDestination();
if (destination != null) {
String msgType = destination.getDestinationTypeAsString();
String topic = destination.toString();
System.out.println(TAG + "addConsumer msgType: " + msgType + " topic: " + topic);
Map<String, String> opts = destination.getOptions();
if (null != opts) {
for (Map.Entry<String, String> entry : opts.entrySet()) {
System.out.println(TAG + "key: " + entry.getKey() + " value: " + entry.getValue());
}
}
if ("Topic".equals(msgType)) {
if (!topic.startsWith("topic://ActiveMQ.Advisory") && !topic.startsWith("topic://$SYS")
&& !topic.equals("topic://")) {
if (!checkDestinationAdmin(destination, context.getClientId(), context.getUserName(), topic, 1)) {
throw new SecurityException(
"User " + context.getUserName() + " is not authorized to read from: " + destination);
}
}
}
}
return super.addConsumer(context, info);
}
/**
* 由于需要授权是否可写消息,因此重写BrokerFilter的 addProducer
* 方法,在该方法中,从访问控制列表中查看是否具有写授权,并调用next引用的 addProducer 方法
*/
@Override
public void addProducer(ConnectionContext context, ProducerInfo info) throws Exception {
System.out.println(TAG + "addProducer(ConnectionContext context, ProducerInfo info)");
ActiveMQDestination destination = info.getDestination();
if (destination != null) {
String msgType = destination.getDestinationTypeAsString();
String topic = destination.toString();
System.out.println(TAG + "addProducer msgType: " + msgType + " topic: " + topic);
Map<String, String> opts = destination.getOptions();
if (null != opts) {
for (Map.Entry<String, String> entry : opts.entrySet()) {
System.out.println(TAG + "key: " + entry.getKey() + " value: " + entry.getValue());
}
}
if ("Topic".equals(msgType)) {
if (!topic.startsWith("topic://ActiveMQ.Advisory") && !topic.startsWith("topic://$SYS")
&& !topic.equals("topic://")) {
if (!checkDestinationAdmin(destination, context.getClientId(), context.getUserName(), topic, 2)) {
throw new SecurityException(
"User " + context.getUserName() + " is not authorized to write to: " + destination);
}
}
}
}
super.addProducer(context, info);
}
@Override
public void send(ProducerBrokerExchange producerExchange, Message messageSend) throws Exception {
System.out.println(TAG + "send(ProducerBrokerExchange producerExchange, Message messageSend)");
ActiveMQDestination destination = messageSend.getDestination();
if (destination != null) {
String msgType = destination.getDestinationTypeAsString();
String topic = destination.toString();
System.out.println(TAG + "send msgType: " + msgType + " topic: " + topic);
Map<String, String> opts = destination.getOptions();
if (null != opts) {
for (Map.Entry<String, String> entry : opts.entrySet()) {
System.out.println(TAG + "key: " + entry.getKey() + " value: " + entry.getValue());
}
}
if ("Topic".equals(msgType)) {
if (!topic.startsWith("topic://ActiveMQ.Advisory") && !topic.startsWith("topic://$SYS")
&& !topic.equals("topic://")) {
if (!checkDestinationAdmin(destination, producerExchange.getConnectionContext().getClientId(),
producerExchange.getConnectionContext().getUserName(), topic, 2)) {
throw new SecurityException("User " + producerExchange.getConnectionContext().getUserName()
+ " is not authorized to write to: " + destination);
}
}
}
}
super.send(producerExchange, messageSend);
}
}
3. 创建 MqttAuthorizationDestinationInterceptor 类实现 DestinationInterceptor 接口,在 intercept(Destination destination) 生成一个订阅的 DestinationFilter
package com.xy.auth;
import org.apache.activemq.broker.Broker;
import org.apache.activemq.broker.ConnectionContext;
import org.apache.activemq.broker.region.Destination;
import org.apache.activemq.broker.region.DestinationInterceptor;
import org.apache.activemq.command.ActiveMQDestination;
public class MqttAuthorizationDestinationInterceptor implements DestinationInterceptor{
private final MqttAuthorizationBroker broker;
public MqttAuthorizationDestinationInterceptor(MqttAuthorizationBroker broker) {
this.broker = broker;
}
@Override
public Destination intercept(Destination destination) {
return new MqttAuthorizationDestinationFilter(destination, broker);
}
@Override
public void remove(Destination destination) {}
@Override
public void create(Broker broker, ConnectionContext context, ActiveMQDestination destination) throws Exception {}
}
4. 创建 MqttAuthorizationDestinationFilter 类继承 DestinationFilter 接口,在 addSubscription 判断订阅的权限:
package com.xy.auth;
import org.apache.activemq.broker.ConnectionContext;
import org.apache.activemq.broker.region.Destination;
import org.apache.activemq.broker.region.DestinationFilter;
import org.apache.activemq.broker.region.Subscription;
import org.apache.activemq.command.ActiveMQDestination;
public class MqttAuthorizationDestinationFilter extends DestinationFilter {
private static final String TAG = " ------ MqttAuthorizationDestinationFilter ";
private final MqttAuthorizationBroker broker;
public MqttAuthorizationDestinationFilter(Destination next, MqttAuthorizationBroker broker) {
super(next);
this.broker = broker;
}
@Override
public void addSubscription(ConnectionContext context, Subscription sub) throws Exception {
System.out.println(TAG + "addSubscription(ConnectionContext context, Subscription sub)");
final ActiveMQDestination destination = next.getActiveMQDestination();
String msgType = destination.getDestinationTypeAsString();
String topic = destination.toString();
System.out.println(TAG + "addSubscription username: " + context.getUserName() + " topic: " + topic);
if ("Topic".equals(msgType)) {
if (!topic.startsWith("topic://ActiveMQ.Advisory") && !topic.startsWith("topic://$SYS")
&& !topic.equals("topic://")) {
if (!broker.checkDestinationAdmin(destination, context.getClientId(), context.getUserName(), topic,
1)) {
throw new SecurityException(
"User " + context.getUserName() + " is not authorized to read from: " + destination);
}
}
}
super.addSubscription(context, sub);
}
}