`

第七章 ActiveMQ 安全认证

 
阅读更多

                                                                  ActiveMQ 安全认证

 

章节导读
  •    如何使用安全认证
  •    如何使用授权
  •    如何创建一个定制的安全插件
  •    使用基于证书的安全认证

              认证就是一个验证某些请求受保护的资源的用户或者实体的完整性的过程.一些常见的认证形式包括纯文本密码,一次性密码设备,智能卡等等.Activemq提供了一些简单的认证和JAAS(java认证和java认证服务),和一个用于自定义安全插件的api.在成功认证之后,就被授权可以访问系统资源了.

 

1.1 认证

       所有在MQ实现的安全概念都是插件的形式实现的.

  • 简单认证插件---直接在xml或者properties文件中授权认证信息.
  • JAAS认证插件----实现了JAAS API,提供了更强力可定制的认证解决方案.

   1.1.1 配置简单认证插件

         在代理的xml中直接配置身份认证凭证是最简单的一种方式.配置的xml如下:

                  

<broker ...>
	<plugins>
		<simpleAuthenticationPlugin>
			<users>
				<authenticationUser username="admin" password="password" groups="admins,publishers,consumers"/>
				<authenticationUser username="publisher" password="password" groups="publishers,consumers"/>
				<authenticationUser username="consumer" password="password" groups="consumers"/>
				<authenticationUser username="guest" password="password" groups="guests"/>
			</users>
		</simpleAuthenticationPlugin>
	</plugins>
</broker>

   代码中的改动就是在创建连接时用带用户名密码的方法.

   

  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展示了如何配置:

<plugins>
	<jaasAuthenticationPlugin  configuration="activemq-domain" />
	      <authorizationPlugin>
		         <map>
			   <authorizationMap>
				<authorizationEntries>
					<authorizationEntry topic=">" read="admins" write="admins" admin="admins" />
						<authorizationEntry topic="STOCKS.>" read="consumers" write="publishers" admin="publishers" />
							<authorizationEntry topic="STOCKS.ORCL" read="guests" />
							<authorizationEntry topic="ActiveMQ.Advisory.>" read="admins,publishers,consumers,guests" write="admins,publishers,consumers,guests" admin="admins,publishers,consumers,guests" />
				</authorizationEntries>
			   </authorizationMap>
			</map>
	   </authorizationPlugin>
</plugins>

 

  • 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换成自己的类路径

           <messageAuthorizationPolicy>
			<bean
			class="com.zcf.activemq.authorization.AuthorizationPolicy"
			xmlns="http://www.springframework.org/schema/beans" />
          </messageAuthorizationPolicy>

      运行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<String> allowedIPAddresses;
	Pattern pattern = Pattern.compile("^/([0-9\\.]*):(.*)");//Ip的正则表达式

	public IPAuthenticationBroker(Broker next, List<String> 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<String> allowedIPAddresses;

	@Override
	public Broker installPlugin(Broker broker) throws Exception {
		return new IPAuthenticationBroker(broker, allowedIPAddresses);
	}

	public List<String> getAllowedIPAddresses() {
		return allowedIPAddresses;
	}

	public void setAllowedIPAddresses(List<String> allowedIPAddresses) {
		this.allowedIPAddresses = allowedIPAddresses;
	}
}

      我们还是需要先把刚才生成的类用maven打成jar包放到lib目录下.     

 

 

     1.3.2  配置插件

        

                  <plugins>
			<bean xmlns="http://www.springframework.org/schema/beans"
			id="ipAuthenticationPlugin"
			class="com.zcf.activemq.security.customerplugin.IPAuthenticationPlugin">
				<property name="allowedIPAddresses">
					<list>
					<value>127.0.0.1</value>
					</list>
				</property>
			</bean>
		</plugins>

       通过运行测试可以发现,除了本地的机器可以运行.其它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文件,

                  

		<sslContext>
			<sslContext keyStore="file:${activemq.base}/conf/mybroker.ks"
			keyStorePassword="XXX"	trustStore="file:${activemq.base}/conf/mybroker.ts"		trustStorePassword="XXX"/>
		</sslContext>
		<transportConnectors>
			<transportConnector name="ssl"
			uri="ssl://localhost:61617?needClientAuth=true" />
		</transportConnectors>

 

             在这里先配置了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

            <plugins>
			<jaasCertificateAuthenticationPlugin configuration="activemq-certificate" />
			<authorizationPlugin>
				<map>
					<authorizationMap>
						<authorizationEntries>
							<authorizationEntry topic=">"	read="admins" write="admins" admin="admins" />
							<authorizationEntry topic="STOCKS.>"	read="consumers"			write="publishers" admin="publishers" />
							<authorizationEntry topic="STOCKS.ORCL"	read="guests" />
							<authorizationEntry topic="ActiveMQ.Advisory.>"	read="admins,publishers,consumers,guests"		write="admins,publishers,consumers,guests" admin="admins,publishers,consumers,guests" />
						
						</authorizationEntries>
					</authorizationMap>
				</map>
			</authorizationPlugin>
		</plugins>

      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
分享到:
评论

相关推荐

    第七章航空管理系统需要的所有jar包

    标题提到的“第七章航空管理系统需要的所有jar包”是指在开发该系统时,开发者收集并整理的一系列Java Archive (JAR) 文件。JAR文件是Java平台特有的归档格式,用于封装多个类文件、资源文件以及元数据,便于分发和...

    Spring 3.x企业应用开发实战第17章

    本章可能提到了Spring与缓存系统(如 EhCache)、消息中间件(如ActiveMQ)或其他服务(如RESTful API)的集成。 以上知识点构成了《Spring 3.x企业应用开发实战》第17章的核心内容,通过学习和实践这些内容,读者...

    第六章 组件通讯下.666666

    9. **安全性**:组件通讯的安全性不容忽视,需要考虑认证、授权、加密以及防止DDoS攻击等措施。 10. **性能优化**:通过缓存、负载均衡、数据压缩、超时设置等方式优化组件间的通信性能。 以上是对组件通讯的一般...

    Spring4实战 18章 STOMP

    STOMP是一种简单、可扩展的网络协议,用于在消息代理(如RabbitMQ或Apache ActiveMQ)与客户端之间传递消息。它为异步、基于消息的应用程序提供了一种统一的接口,使得开发者能够轻松地处理不同平台之间的通信。 ...

    Java信息管理系统开发实例导航_网络即时通信系统

    6. **安全机制**:即时通信需要保证数据的安全性,因此可能涉及到SSL/TLS加密,以及用户认证和权限控制等安全措施。 7. **事件驱动模型**:Java的Event-Driven编程模型可以帮助实现高效的通信,如使用Java NIO(非...

Global site tag (gtag.js) - Google Analytics