阅读更多
ActiveMQ 安全认证
章节导读
- 如何使用安全认证
- 如何使用授权
- 如何创建一个定制的安全插件
- 使用基于证书的安全认证
认证就是一个验证某些请求受保护的资源的用户或者实体的完整性的过程.一些常见的认证形式包括纯文本密码,一次性密码设备,智能卡等等.Activemq提供了一些简单的认证和JAAS(java认证和java认证服务),和一个用于自定义安全插件的api.在成功认证之后,就被授权可以访问系统资源了.
1.1 认证
所有在MQ实现的安全概念都是插件的形式实现的.
- 简单认证插件---直接在xml或者properties文件中授权认证信息.
- JAAS认证插件----实现了JAAS API,提供了更强力可定制的认证解决方案.
1.1.1 配置简单认证插件
在代理的xml中直接配置身份认证凭证是最简单的一种方式.配置的xml如下:
代码中的改动就是在创建连接时用带用户名密码的方法.
connection = factory.createConnection(username, password);
使用文本密码验证的安全性没办法保证,除非加上ssl. 要保证安全的情况下JAAS 插件更适合.
1.1.2 配置JAAS插件
JAAS提供可插入式的认证,这意味着Mq会使用同样的认证api.这需要去实现javax.security.auth.spi.LoginModule 接口以及Mq配置文件的改变.
1.修改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";
};
PropertiesLoginModule用于jaas登陆模块,定义user的文件在
users.properties中,定义group的文件在groups.properties中
user.properties
admin=admin
publisher=password
consumer=password
guest=password
group.properties
admins=admin
publishers=admin,publisher
consumers=admin,publisher,consumer
guests=guest
1.2 授权
基于认证,如果需要控制更细粒度的权限,就需要授权.
ActiveMq提供了两种级别的授权:操作级别和消息级别的授权,这两种级别的认证提供更详细的级别控制.
1.2.1 Destination级别的授权
对于Destination有三种类型的用户级别的操作 :读,写,管理.下面的xml展示了如何配置:
- admins对所有topic有所有的权限
- 消费者可以消费,生产者可以生产消息在STOCK的目录下
- guests组对于STOCKS.ORCL只有读权限
1.2.1 消息级别的授权
在某些情况下,你可能只想允许访问一个destination中的某些特别的消息.要实现这些,我们先要实现org.apache.activemq.security.MessageAuthorizationPolicy接口.
例子如下:只有地址时tcp://127.0.0.1开头的地址才能消费
public class AuthorizationPolicy implements MessageAuthorizationPolicy {
private static final Log LOG = LogFactory.getLog(AuthorizationPolicy.class);
@Override
public boolean isAllowedToConsume(ConnectionContext context, Message message) {
LOG.info(context.getConnection().getRemoteAddress());
String remoteAddress = context.getConnection().getRemoteAddress();
LOG.info("地址:" + remoteAddress);
if (remoteAddress.startsWith("127.0.0.1")) {
LOG.info("Permission to consume granted");
return true;
} else {
LOG.info("Permission to consume denied");
return false;
}
}
}
这个策略必须被打成jar包放入lib/目录下.可以用maven直接打包
执行mvn clean install
然后找到打包好的jar(ActiveMqLearn-0.0.1-SNAPSHOT.jar)复制到lib目录下,然后在配置文件中加入以下的配置,bean节点的class换成自己的类路径
运行consumer,就会在控制台出现以下信息
如果把remoteAddress.startsWith("127.0.0.1") 改成remoteAddress.startsWith("tcp://127.0.0.1"),那么消息就会被允许消费.
1.3 建立一个定制的安全性插件
brokerFilter提供拦截代理级别的操作.包括增加消费者和生产者,提交事务,增加移除和其它代理的连接.
1.3.1 实现插件
为了限制(基于IP)到代理的连接.我们可以创建一个类,覆盖BrokerFilter.addConnection().方法.
首先创建一个IP连接器
public class IPAuthenticationBroker extends BrokerFilter {
List allowedIPAddresses;
Pattern pattern = Pattern.compile("^/([0-9\\.]*):(.*)");//Ip的正则表达式
public IPAuthenticationBroker(Broker next, List allowedIPAddresses) {
super(next);
this.allowedIPAddresses = allowedIPAddresses;
}
@Override
public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {
String remoteAddress = context.getConnection().getRemoteAddress();
Matcher matcher = pattern.matcher(remoteAddress);
if (matcher.matches()) {
String ip = matcher.group(1);
if (!allowedIPAddresses.contains(ip)) {
throw new SecurityException("Connecting from IP address " + ip + " is not allowed");
}
} else {
throw new SecurityException("Invalid remote address " + remoteAddress);
}
super.addConnection(context, info);
}
}
然后创建一个插件安装类,它是用于暴露配置以及安装插件到ActiveMq.
package com.zcf.activemq.security.customerplugin;
import java.util.List;
import org.apache.activemq.broker.Broker;
import org.apache.activemq.broker.BrokerPlugin;
public class IPAuthenticationPlugin implements BrokerPlugin {
List allowedIPAddresses;
@Override
public Broker installPlugin(Broker broker) throws Exception {
return new IPAuthenticationBroker(broker, allowedIPAddresses);
}
public List getAllowedIPAddresses() {
return allowedIPAddresses;
}
public void setAllowedIPAddresses(List allowedIPAddresses) {
this.allowedIPAddresses = allowedIPAddresses;
}
}
我们还是需要先把刚才生成的类用maven打成jar包放到lib目录下.
1.3.2 配置插件
127.0.0.1
通过运行测试可以发现,除了本地的机器可以运行.其它ip的都会被阻止.
1.4 基于证书的安全认证
truststore是放信任的证书的一个store,truststore和keystore的性质是一样的,都是存放key的一个仓库,区别在于,truststore里存放的是只包含公钥的数字证书,代表了可以信任的证书,而keystore是包含私钥的。
Server需要:KeyStore:保存服务端的私钥;Trust KeyStore:保存客户端的授权证书
Client需要: KeyStore:保存客户端的私钥; Trust KeyStore:保存服务端的授权证书
1.4.1 生成证书和truststore
- 打开cmd命令窗口,cd到%JAVA_HOME%\bin\目录下
- 通过命令 keytool -genkey -alias myproducer -keyalg RSA -keystore myproducer.ks keytool -genkey -alias myconsumer-keyalg RSA -keystore myconsumer.ks生成生产者和消费者的私钥
- 通过命令 keytool -genkey -alias mybroker-keyalg RSA -keystore mybroker.ks 生成代理的私钥
- 通过命令 keytool -export -alias myproducer -keystore myproducer.ks -file myproducer_cert keytool -export -alias ,myconsumer -keystore myconsumer.ks -file myconsumer_cert导出生产者消费者证书
- 通过命令 keytool -export -alias mybroker -keystore mybroker.ks -file mybroker_cert生成代理的证书
- 通过命令 keytool -import -alias myproducer -keystore mybroker.ts -file myproducer_cert,keytool -import -alias myconsumer -keystore mybroker.ts -file myconsumer_cert把生产者和消费者的证书加入代理的truststore中去
- 通过命令 keytool -import -alias mybroker -keystore myclient.ts -file mybroker_cert把代理的证书加入客户端信任库中
把mybroker.ks和mybroker.ts放入/conf/文件夹下
把myclient.ts和myconsumer.ks,myproducer.ks放入项目中,具体位置如下
1.4.2 配置代理
修改activemq.xml文件,
在这里先配置了ssl的链接,needClientAuth参数代表需要在客户端连接的时候检查证书是否在信任库中.
sslContext中配置了代理私钥的证书路径以及密码,信任库的路径以及密码
生产者代码:
package com.zcf.activemq.security.cert;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Session;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import org.apache.activemq.ActiveMQSslConnectionFactory;
public class Publisher {
private static String brokerURL = "ssl://localhost:61617";
private static transient ActiveMQSslConnectionFactory factory;
private transient Connection connection;
private transient Session session;
private transient MessageProducer producer;
private static int count = 10;
private static int total;
private static int id = 1000000;
private String jobs[] = new String[] { "suspend", "delete" };
public Publisher() throws JMSException, Exception, NoSuchAlgorithmException, KeyStoreException, CertificateException, GeneralSecurityException, IOException {
factory = new ActiveMQSslConnectionFactory(brokerURL);
factory.setKeyAndTrustManagers(getKeyManagers("myproducer.ks", "密码"),
getTrustManagers("myclient.ts"),
new java.security.SecureRandom());
connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(null);
}
public void close() throws JMSException {
if (connection != null) {
connection.close();
}
}
public static void main(String[] args) throws NoSuchAlgorithmException, KeyStoreException, Exception {
Publisher publisher = new Publisher();
while (total < 1) {
for (int i = 0; i < count; i++) {
publisher.sendMessage();
}
total += count;
System.out.println("Published '" + count + "' of '" + total + "' job messages");
try {
Thread.sleep(1000);
} catch (InterruptedException x) {
}
}
publisher.close();
}
public void sendMessage() throws JMSException {
int idx = 0;
while (true) {
idx = (int) Math.round(jobs.length * Math.random());
if (idx < jobs.length) {
break;
}
}
String job = jobs[idx];
Destination destination = session.createTopic("STOCKS." + job);
Message message = session.createObjectMessage(id++);
System.out.println("Sending: id: " + ((ObjectMessage) message).getObject() + " on queue: " + destination);
producer.send(destination, message);
}
private KeyManager[] getKeyManagers(String keyStore, String keyStorePassword)
throws java.security.NoSuchAlgorithmException, java.security.KeyStoreException,
java.security.GeneralSecurityException, java.security.cert.CertificateException, java.io.IOException,
java.security.UnrecoverableKeyException {
System.out.println("Initiating KeyManagers");
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(ClassLoader.getSystemResourceAsStream(keyStore), keyStorePassword.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, keyStorePassword.toCharArray());
System.out.println("Initiated KeyManagers");
return kmf.getKeyManagers();
}
private TrustManager[] getTrustManagers(String trustStore) throws java.security.NoSuchAlgorithmException,
java.security.KeyStoreException, java.io.IOException, java.security.GeneralSecurityException {
System.out.println("Initiating TrustManagers");
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(ClassLoader.getSystemResourceAsStream(trustStore), null);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
System.out.println("Initiated TrustManagers");
return tmf.getTrustManagers();
}
}
消费者代码:
package com.zcf.activemq.security.cert;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.jms.Session;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import org.apache.activemq.ActiveMQSslConnectionFactory;
import org.apache.log4j.Logger;
public class Consumer {
private static Logger logger = Logger.getLogger(Consumer.class);
private static String brokerURL = "ssl://localhost:61617";
private static transient ActiveMQSslConnectionFactory factory;
private transient Connection connection;
private transient Session session;
private String jobs[] = new String[] { "suspend", "delete" };
public Consumer() throws JMSException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException, CertificateException, GeneralSecurityException, IOException {
factory = new ActiveMQSslConnectionFactory(brokerURL);
factory.setKeyAndTrustManagers(getKeyManagers("myconsumer.ks", "8797393"),
getTrustManagers("myclient.ts"),
new java.security.SecureRandom());
connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
}
public void close() throws JMSException {
if (connection != null) {
connection.close();
}
}
public static void main(String[] args) throws JMSException, InterruptedException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException, CertificateException, GeneralSecurityException, IOException {
Consumer consumer = new Consumer();
for (String job : consumer.jobs) {
Destination destination = consumer.getSession().createTopic("STOCKS." + job);
MessageConsumer messageConsumer = consumer.getSession().createConsumer(destination);
messageConsumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
try {
//do something here
System.out.println( ((ObjectMessage)message).getObject());
logger.info(((ObjectMessage)message).getObject());
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
public Session getSession() {
return session;
}
private KeyManager[] getKeyManagers(String keyStore, String keyStorePassword)
throws java.security.NoSuchAlgorithmException, java.security.KeyStoreException,
java.security.GeneralSecurityException, java.security.cert.CertificateException, java.io.IOException,
java.security.UnrecoverableKeyException {
System.out.println("Initiating KeyManagers");
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(ClassLoader.getSystemResourceAsStream(keyStore), keyStorePassword.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, keyStorePassword.toCharArray());
System.out.println("Initiated KeyManagers");
return kmf.getKeyManagers();
}
private TrustManager[] getTrustManagers(String trustStore) throws java.security.NoSuchAlgorithmException,
java.security.KeyStoreException, java.io.IOException, java.security.GeneralSecurityException {
System.out.println("Initiating TrustManagers");
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(ClassLoader.getSystemResourceAsStream(trustStore), null);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
System.out.println("Initiated TrustManagers");
return tmf.getTrustManagers();
}
}
1.4.3 基于证书的授权配置
在上面的activemq.xml的基础上加上下面的XML
login.config文件修改成,activemq-certificate对应jaasCertificateAuthenticationPlugin节点中的 configuration属性
activemq-certificate {
org.apache.activemq.jaas.TextFileCertificateLoginModule
required debug=true
org.apache.activemq.jaas.textfiledn.user="users.properties"
org.apache.activemq.jaas.textfiledn.group="groups.properties";
};
users.proerties修改成
admin=admin
publisher=password
consumer=password
guest=password
sslconsumer=CN=MYCONSUMER, OU=HYT, O=HUIYUNTONG, L=FZ, ST=FJ, C=CN
sslpublisher=CN=MYPRODUCER, OU=HYT, O=HUIYUNTONG, L=FZ, ST=FJ, C=CN
sslconsumer,sslconsumer用户后面对应的密码属性都是生成生产者消费者过程中填写的信息(属性之间逗号之后要有空隔)
groups.proerties修改成
admins=admin
publishers=admin,publisher,sslpublisher
consumers=admin,publisher,consumer,sslconsumer
guests=guest
这样就可以控制更细粒度的权限了.
- 大小: 25.2 KB
- 大小: 28.3 KB
- 大小: 27.1 KB