6.3建立定制安全插件

至今这章关注于ActiveMQ内置的安全特性。虽然这些特性应该为majority用户提过了足够的functionality,但有一个更强大的特性。如前面所说,ActiveMQ 插件API是极度灵活的并且有无尽的可能性。这个功能的灵活性由BrokerFilter这个类而来。这个类提供了intercept许多可用的代理级别操作的能力。to name a few,代理操作包括了例如添加代理的consumers和producers,在提交transactions到代理,和添加删除到代理的连接.通过扩展BrokerFilter类并重写给定的操作的方法可以添加定制功能。
虽然ActiveMQ插件API不是仅和安全相关,实现一个主要目的是操作定制的安全特性的类是可行的。所以如果你有安全需求但是前面的安全特性不能满足,你可能应该考虑为你的需求建立一个定制方案。根据你的需求,有两种选择是可用的:
■实现一个JAAS登录模块--若你已经在你的java应用中使用JAAS那就是个好机会。在这种情况下,你会自然地尝试重用所有的工作到安全化ActiveMQ代理。因为JAAS不是该书的主题,我们将不会更深入这个主题。
■为了处理安全性实现一个定制的插件--ActiveMQ提供了一个flexible和generic的插件机制。你能为任何事情,包括定制安全插件建立你自己的插件。所以如果你的需求无法通过实现JAAS模块来满足,写个自制插件is a way to go.
在该节我们将描述如何写一个简单的安全插件,它能authorize代理连接,仅允许一些IP地址。这个观念不复杂但是足够好,它能让你从安全的角度感受BrokerFilter。

6.3.1实现插件
为了根据IP地址限制到代理的连接,我们将创建一个名为IPAuthenticationBroker的类来重写BrokerFilter.addConnection()方法。这个方法的实现将用作对IP地址的简单检查,它使用了一个regular的表达式来确定是否允许连接。下面列出了IPAuthenticationBroker的实现:
Listing 6.4 IPAuthenticationBroker class—custom broker implementation
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);
  }
}

这个BrokerFilter类定义了一些方法,他们拦截了代理的操作,这些操作有例如添加一个connection,删除一个subscriber,等等。在IPAuthenticationBroker类里,addConnection()方法被重写来建立一些逻辑来检查连接的客户端地址是否位于允许连接的IP地址列表内。如果该IP地址被允许连接,这个调用被delegated到BrokerFilter. addConnection()方法。如果不被允许连接则抛出异常。
在IPAuthenticationBroker类中的另外的一个注意点是它的构造器调用了BrokerFilter的构造器。这个调用了serves来建立interceptions的链,这样就能在全链中建立合适的cascading。如果你建立了你自己的BrokerFilter实现不要忘了这么做。
目前的插件逻辑已经被实现以后,插件必须被配置和安装。为了这个目的,一个BrokerPlugin的实现将被创建。BrokerPlugin被用来expose插件的配置并且安装插件到ActiveMQ代理。为了配置和安装IPAuthenticationBroker,IPAuthenticationPlugin类被如下创建:
Listing 6.5 IPAuthenticationPlugin class—custom plug-in implementation
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;
  }
}

IPAuthenticationBroker.installPlugin()被用来实例化插件和为chain中的下一个插件返回一个新的intercepted代理。注意IPAuthenticationPlugin也包含getter和setter方法用来配置IPAuthenticationBroker。在ActiveMQ的XML配置文件中这个setter和getter方法将通过String beans-style XML 配置起作用(你即将看到)。
6.3.2配置代理
现在我们已经实现了代理,让我们看看如何通过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>

<broker>元素提供了插件元素的声明。使用了IPAuthenticationPlugin只有那些IP地址是127.0.0.1 (the localhost)的客户端能连接到代理。
6.3.3测试插件
现在所需做的是测试插件。这里有个命令可以拷贝示例JAR文件(因为它包含了插件)并使用IPAuthenticationPlugin和IPAuthenticationBroker启动ActiveMQ:

$ 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
...
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) is
starting
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
...

现在运行客户端来从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
...

如果一个连接尝试不是从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
...

虽然这个示例更复杂,但它成功地展示了BrokerFilter类所提供的能量。仅仅想象一下这个插件机制为整合现有定制的安全需求是多么的灵活。这个示例侧重在安全上,但是许多其他的操作能通过使用patten illustrated来定制。

猜你喜欢

转载自flxchy4.iteye.com/blog/1727074
6.3