6.3 创建自定义安全插件

博客已搬家, 更好的阅读体验,请移步: http://www.jack-yin.com/english/translation/activemq-in-action/1089.html

 

6.3 Building a custom security plug-in

6.3 创建自定义安全插件

 

So far this chapter has focused on the built-in security features in ActiveMQ. Though

these features should provide enough functionality for the majority of users, an even

more powerful feature is available. As stated previously, the ActiveMQ plug-in API is

extremely flexible and the possibilities are endless. The flexibility in this functionality

comes from the BrokerFilter class. This class provides the ability to intercept many

of the available broker-level operations. Broker operations include such items as adding

consumers and producers to the broker, committing transactions in the broker,

and adding and removing connections to the broker, to name a few. Custom functionality

can be added by extending the BrokerFilter class and overriding a method for a

given operation.

 

到目前为止,本章讨论的内容都是ActiveMQ自带的安全功能.尽管这些自带的功能已足够绝大多数用户使用,

但是ActiveMQ还提供了一种更加强大的功能.前面已经提到,ActiveMQ的插件API具有强大的灵活性,其支持的

功能是无穷无尽的.ActiveMQ的灵活性来自于BrokerFilter类的功能.该类提供了一种拦截大多数可用代理的

各种操作功能.代理的操作包扩:为代理增加消息生产者和消费者,提交代理的事务,增加/移除到代理的连接,

等等.可以通过扩展BrokerFilter类并且为特定的操作覆盖相关的方法来实现自定义功能.

 

Though the ActiveMQ plug-in API isn’t concerned solely with security, implementing

a class whose main purpose is to handle a custom security feature is achievable. So

if you have security requirements that can’t be met using the previous security features,

you may want to consider developing a custom solution for your needs. Depending

on your needs, two choices are available:

 

尽管ActiveMQ的插件API不仅仅关注安全机制,但是实现一个主要用于处理自定义安全的类是可行的.

因此,如果你有安全方面的需求且该需求不能使用前面介绍的ActiveMQ自带的安全机制来解决,你可以

考虑使用自定义功能来实现自己的需求.基于你的需求,有两种方式可供选择:

 

 Implement a JAAS login module—There’s a good chance that you’re already using

JAAS in your Java applications. In this case, it’s only natural that you’ll try to

reuse all that work for securing the ActiveMQ broker, too. Since JAAS isn’t the

main topic of this book, we won’t dive any deeper into this topic than we

already have.

 

方式一:实现一个JAAS登陆模块 -- 如果你已经在你的应用程序中使用JAAS的话,这是一个不错的选择.

这种情况下,你可以很自然的想到在ActiveMQ代理中重用已实现的JASS登陆模块.因为JAAS不是本书讨论的

主题,我们不打算更深入的介绍JAAS.

 

 Implement a custom plug-in for handling security—ActiveMQ provides a flexible

generic plug-in mechanism. You can create your own custom plug-ins for just

about anything, including custom security plug-ins. So if you have requirements

that can’t be met by implementing a JAAS module, writing a custom plug-in is

the way to go.

 

方式二:实现自定义的插件为处理ActiveMQ安全提供了一个灵活的机制.你可以创建自定义的插件来处理

任何事情,当包含自定义的安全插件.因此假如你有一个不能通过实现JAAS登陆模块来完成需求,你可以写

一个自定义插件来完成.

 

In this section we’ll describe how to write a simple security plug-in that authorizes broker

connections only from a certain set of IP addresses. The concept isn’t complex but

is good enough to give you a taste of the BrokerFilter with an angle toward security.

 

本节中我们将介绍如何通过写一个简单的安全插件来限制只有IP地址属于指定IP地址集合的客户端才能连接

到代理.这个插件涉及到的概念不是很复杂,但是作为在安全角度尝试BrokerFilter的应用来说已经相当好了.

 

6.3.1 Implementing the plug-in

6.3.1 实现插件

 

In order to limit connectivity to the broker based on IP address, we’ll create a class

named IPAuthenticationBroker to override the BrokerFilter.addConnection()

method. The implementation of this method will perform a simple check of the IP

address using a regular expression to determine the ability to connect. The following

listing shows the implementation of the IPAuthenticationBroker class.

 

为了根据客户端的IP地址来限制到代理的连接,我们创建一个名称为IPAuthenticationBroker的方法以

覆盖BrokerFilter.addConnection()方法.该方法中将使用正则表达式简单的检查客户端的IP地址以便

决定客户端能否连接到代理.下面是IPAuthenticationBroker方法的代码清单:

 

Listing 6.4 IPAuthenticationBroker class—custom broker implementation

清单6.4 IPAuthenticationBroker类 -- 实现自定义代理

 

public class IPAuthenticationBroker extends BrokerFilter 

{

  List<String> allowedIPAddresses;

  Pattern pattern = Pattern.compile("^/([0-9\\.]*):(.*)");

 

  public IPAuthenticationBroker(Broker next, List<String> allowedIPAddresses) 

  {

    super(next);

    this.allowedIPAddresses = allowedIPAddresses;

  }

  

  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);

  }

}

 

The BrokerFilter class defines methods that intercept broker operations such as adding

a connection, removing a subscriber, and so forth. In the IPAuthenticationBroker

class, the addConnection() method is overridden to create some logic that checks

whether the address of a connecting client falls within a list of IP addresses that are

allowed to connect. If that IP address is allowed to connect, the call is delegated to the

BrokerFilter. addConnection() method. If that IP address isn’t allowed to connect,

an exception is thrown.

 

BrokerFilter类定义了一些方法,这些方法可以拦截代理的一些操作,比如:增加一个连接,移除一个消息

订阅者,等等.在IPAuthenticationBroker类中,addConnection()方法被覆盖了以便创建一些检查客户端

IP地址是否在允许IP地址列表之内的逻辑.如果当前的IP地址可以连接到代理,对该方法的调用将转移给

BrokerFilter类的addConnection() 方法来处理.如果当前的IP地址不允许连接,则会抛出一个异常.

 

One additional item of note in the IPAuthenticationBroker class is that its constructor

calls the BrokerFilter’s constructor. This call serves to set up the chain of

interceptors so that the proper cascading will take place through the chain. Don’t forget

to do this if you create your own BrokerFilter implementation.

 

关于IPAuthenticationBroker类需要额外注意的是,该类的构造函数调用了BrokerFilter类的构造函数.

这样调用设置了拦截链,以便建立正确的拦截链级联.在创建你自己的BrokerFilter实现时,不要忘记

在构造函数中做这种处理.

 

After the actual plug-in logic has been implemented, the plug-in must be configured

and installed. For this purpose, an implementation of the BrokerPlugin will be

created. The BrokerPlugin is used to expose the configuration of a plug-in and also

to install the plug-in into the ActiveMQ broker. In order to configure and install the

IPAuthenticationBroker, the IPAuthenticationPlugin class is created as shown in

the following listing.

 

当实现了插件的真正逻辑后,还必须要配置和安装插件.为此,需要实现BrokerPlugin,该类用于将一个

插件暴露给ActiveMQ代理,以便代理能够安装它.为了配置和安装插件,创建IPAuthenticationPlugin

类,其代码如下所示:

 

Listing 6.5 IPAuthenticationPlugin class—custom plug-in implementation

清单6.5 IPAuthenticationPlugin类 -- 自实现定义插件

 

public class IPAuthenticationPlugin implements BrokerPlugin 

{

  List<String> allowedIPAddresses;

  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;

  }

}

 

 

The IPAuthenticationBroker.installPlugin() method is used to instantiate the

plug-in and return a new intercepted broker for the next plug-in in the chain. Note

that the IPAuthenticationPlugin class also contains getter and setter methods used

to configure the IPAuthenticationBroker. These setter and getter methods are then

available via a Spring beans–style XML configuration in the ActiveMQ XML configuration

file (as you’ll see in a moment).

 

IPAuthenticationBroker的installPlugin()方法用来初始化插件然后返回一个被拦截的代理,

该代理用于拦截链中的下一个插件.需要注意的是,IPAuthenticationPlugin还包含getter和

setter方法用于配置IPAuthenticationBroker类--setter和getter方法可用于ActiveMQ的Spring 

beans风格的XML配置文件(稍后将看到).

 

6.3.2 Configuring the plug-in

6.3.2 配置插件

 

Now that we’ve implemented the plug-in, let’s see how we can configure it using

the ActiveMQ XML configuration file. The following listing shows how the

IPAuthenticationPlugin class is used in configuration.

实现了自定义插件后,让我们再看看如何使用该自定义插件配置ActiveMQ的XML配置文件.

下面的配置代码清单中显示了如何在配置文件中配置IPAuthenticationPlugin.

 

Listing 6.6 Configuring the custom plug-in

<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.base}/data">

  <plugins>

    <bean xmlns="http://www.springframework.org/schema/beans" id="ipAuthenticationPlugin" class="org.apache.activemq.book.ch6.IPAuthenticationPlugin">

      <property name="allowedIPAddresses">

        <list>

          <value>127.0.0.1</value>

        </list>

      </property>

    </bean>

  </plugins>

 

  <transportConnectors>

    <transportConnector name="openwire" uri="tcp://localhost:61616" />

  </transportConnectors>

</broker>

 

The <broker> element provides the plugins element for declaring plug-ins. Using

the IPAuthenticationPlugin, only those clients connecting from the IP address

127.0.0.1 (the localhost) can actually connect to the broker.

 

<broker>元素的plugins子元素用于定义插件.使用IPAuthenticationPlugin插件后,连接到

代理的客户端中只有IP地址为127.0.0.1(localhost)的客户端才能连到代理.

 

6.3.3 Testing the plug-in

6.3.3 测试插件

 

All that needs to be done now is to test the plug-in. Here’s the command to copy the

examples JAR file into place (because it contains the plug-in) and the command to

start up ActiveMQ using the IPAuthenticationPlugin and the IPAuthentication-

Broker:

现在要做的就是测试常见了.下面的命令首先,复制示例中的jar包(因为该jar包中含有插件类)到

合适的位置,然后启动使用了IPAuthenticationPlugin和IPAuthentication的代理:

 

$ cp target/activemq-in-action-examples.jar ${ACTIVEMQ_HOME}/lib/

 

$ {ACTIVEMQ_HOME}/bin/activemq console xbean:src/main/resources/org/apache/activemq/book/ch6/activemq-custom.xml

 

(译注:window中的命令 %ACTIVEMQ_HOME%/bin/activemq xbean:src/main/resources/org/apache/activemq/book/ch6/activemq-custom.xml)

(译注:使用ActiveMQ5.8测试,原始源码中的配置文件貌似有问题,已改成下面这样:

activemq-custom.xml

<beans 

  xmlns="http://www.springframework.org/schema/beans"

  xmlns:amq="http://activemq.apache.org/schema/core"

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

  http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd">

 

 

<!-- Allows us to use system properties as variables in this configuration file -->

<bean

class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />

 

<broker xmlns="http://activemq.apache.org/schema/core"

brokerName="localhost" dataDirectory="${activemq.base}/data" >

 

<!-- The transport connectors ActiveMQ will listen to -->

<transportConnectors>

<transportConnector name="openwire"

uri="tcp://localhost:61616" />

</transportConnectors>

    

    <plugins>

      <bean xmlns="http://www.springframework.org/schema/beans" id="ipAuthenticationPlugin" class="org.apache.activemq.book.ch6.IPAuthenticationPlugin">

        <property name="allowedIPAddresses">

          <list>

            <value>127.0.0.1</value>

          </list>

        </property>

      </bean>

    </plugins>

  

</broker>

 

 

 

</beans>

)

 

...

Loading message broker from: xbean:src/main/resources/org/apache/activemq/book/ch6/activemq-custom.xml

...

23:22:46,982 | INFO | PListStore:/Users/bsnyder/amq/apache-activemq-5.4.1/data/localhost/tmp_storage started

23:22:47,156 | INFO | JMX consoles can connect to service: jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi

23:22:47,159 | INFO | Using Persistence Adapter: KahaDBPersistenceAdapter[/Users/bsnyder/amq/apache-activemq-5.4.1/data/localhost/KahaDB]

23:22:48,033 | INFO | KahaDB is version 2

23:22:48,083 | INFO | ActiveMQ 5.4.1 JMS Message Broker (localhost) isstarting

23:22:48,084 | INFO | For help or more information please see:http://activemq.apache.org/

23:22:48,234 | INFO | Scheduler using directory:/Users/bsnyder/amq/apache-activemq-5.4.1/data/localhost/scheduler

23:22:48,275 | INFO | JobSchedulerStore:/Users/bsnyder/amq/apache-activemq-5.4.1/data/localhost/scheduler started

23:22:48,317 | INFO | Listening for connections at: tcp://localhost:61616

23:22:48,317 | INFO | Connector openwire Started

23:22:48,319 | INFO | ActiveMQ JMS Message Broker

(localhost, ID:mongoose.local-49947-1289974968106-0:0) started

...

 

Now run the client to connect to ActiveMQ from the localhost and everything should

be working fine. See the following output:

接着,使用下面的命令,在localhost上运行客户端以连接到ActiveMQ,程序运行应该一切正常.命令后面面是程序输出信息:

 

$ mvn exec:java -Dexec.mainClass=org.apache.activemq.book.ch3.portfolio.Publisher -Dexec.args="CSCO ORCL"

...

Sending: {price=0.7137712112409276, stock=ORCL, offer=0.7144849824521684,up=true} on destination: topic://STOCKS.ORCL

Sending: {price=0.7127548328743109, stock=ORCL, offer=0.7134675877071851,up=false} on destination: topic://STOCKS.ORCL

Sending: {price=0.710497871629952, stock=ORCL, offer=0.711208369501582,up=false} on destination: topic://STOCKS.ORCL

Sending: {price=0.7167766362460622, stock=ORCL, offer=0.7174934128823083,up=true} on destination: topic://STOCKS.ORCL

Sending: {price=54.586310464064766, stock=CSCO, offer=54.64089677452883,up=false} on destination: topic://STOCKS.CSCO

Sending: {price=54.45678231194236, stock=CSCO, offer=54.5112390942543,up=false} on destination: topic://STOCKS.CSCO

Sending: {price=0.7134830573922482, stock=ORCL, offer=0.7141965404496403,up=false} on destination: topic://STOCKS.ORCL

Sending: {price=0.7125898470778729, stock=ORCL, offer=0.7133024369249507,up=false} on destination: topic://STOCKS.ORCL

Sending: {price=0.7106363691848542, stock=ORCL, offer=0.711347005554039,up=false} on destination: topic://STOCKS.ORCL

Sending: {price=54.99339386523512, stock=CSCO, offer=55.04838725910035,up=true} on destination: topic://STOCKS.CSCO

Published '10' of '10' price messages

...

 

If a connection attempt is made from any host other than the localhost, you can

expect to see the following output including the exception:

如果有非localhost的客户端尝试连接到代理,你将看到下面的输出中显示的异常信息:

 

$ mvn exec:java -Dexec.mainClass=org.apache.activemq.book.ch3.portfolio.Publisher -Dexec.args="CSCO ORCL"

...

Exception in thread "main"

javax.jms.JMSException: Connecting from IP address 192.168.10.10 is not

allowed

...

 

Although this example was more complex, it serves as a good demonstration of the

power provided by the BrokerFilter class. Just imagine how flexible this plug-in

mechanism is for integrating with existing custom security requirements. This example

was focused on a security example, but many other operations can be customized

by using the pattern illustrated here.

 

这个例子更加复杂一些,用它来示例BrokerFilter类提供的强大功能十分合适.可以设想一下使用这种

插件机制来集成各种自定义的安全需求是多么的灵活.这个例子仅仅关注安全方面的内容,然而使用这种

插件模式可以进行更多的自定义操作.

 

你可能感兴趣的:(自定义)