6.2 Authorization
6.2 授权
To build upon authentication, consider a use case requiring more fine-grained control
over clients to authorize certain tasks. In most stock trading applications, only specific
applications can write to a given destination. After all, you wouldn’t want any old
application publishing stock prices to the STOCKS.* destinations. Only an authenticated
and authorized application should have this ability.
创建完验证后,考虑下面这种情况:需要对客户端进行更细粒度的控制以控制对特定任务的授权.
在大多数股票交易程序中,只有特定的应用程序可以往给定的目的地写入数据.毕竟,你不希望
任何老程序还能往STOCKS.*消息目的地发布股票价格信息,只有验证和授权过后的程序才可以
具有发布股票消息到目的地的功能.
For this reason, ActiveMQ provides two levels of authorization: operation-level
authorization and message-level authorization. These two types of authorization provide
a more detailed level of control than simple authentication. This section discusses
these two types of authorization and walks through some examples to
demonstrate each.
为此,ActiveMQ提供两种级别的授权:操作级别授权和消息级别授权.这两者授权方式提供了
比简单认证方式更细致的访问控制.本节将讨论这两种授权方式并通过实例加以说明.
6.2.1 Destination-level authorization
6.2.1 目的地级别的授权
There are three types of user-level operations with JMS destinations:
Read—The ability to receive messages from the destination
Write—The ability to send messages to the destination
Admin—The ability to administer the destination
针对JMS消息目的地一共有三种用户操作权限:
读 -- 具有从特定目的地接收消息的权限
写 -- 具有发送消息到特定目的地的权限
管理 -- 具有管理消息目的地的权限
Through these well-known operations, you can control the ability to perform the
operations. Using the ActiveMQ XML configuration file, such authorization can be
easily defined. Take a look at the following listing to add some operation-specific
authorization to some destinations.
你可以控制上述三种常用的操作.使用ActiveMQ的XML配置文件可以很容易的配置上面的授权方式.
下面的实例代码为一些消息目的地添加了几种特殊的操作授权:
Listing 6.2 Configuring destination-level authorization
...
<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>
...
In the listing, the JAAS authorization plug-in has been defined and pointed at the
activemq-domain configuration in the login.config file. It has also been provided with
a map of authorization entries. When configuring the map of authorization entries,
the first task is to define the destination to be secured. This is achieved through the
use of either a topic or a queue attribute on the entry. The next task is to declare
which users and/or groups have privileges for operations on that destination.
在上面的代码清单配置了JAAS授权插件并指向login.config文件中名称为activemq-domain的配置.
并且,该配置还提供了一个包含一系列的授权实体的授权映射.在配置授权实体映射时,首要的工作
是定义受安全保护的消息目的地.可以使用authorizationEntry的topic或者queue来定义受安全保护
的消息目的地名称,接下来是要配置哪个用户和/或群组具有操作这个消息目的地的权限.
A handy feature is the ability to define the destination value using wildcards. For
example, STOCKS.> means the entry applies to all destinations in the STOCKS path
recursively. You can find more information on wildcards in chapter 11. Also, the
authorization operations will accept either a single group or a comma-separated list of
groups as a value.
在配置消息目的地时,可以使用通配符来简化配置.例如,"STOCKS.>" 表示该授权实体可以递归地应用到
路径以STOCKS开头的目的地中.更多有关于通配符的内容请参阅第11章内容.同时,配置中操作属性值可以是
单一的群组名称或者一个以逗号分割的群组名称集合.
Considering this explanation, the configuration used in the previous example can
be translated as follows:
作为配置说明,上面的配置可以被翻译为:
Users from the admins group have full access to all topics
来自admins群组中的用户具有所有主题的操作权限.
Consumers can consume and publishers can publish to the destinations in the STOCKS path
消息消费者可以消费路径以STOCKS开头的主题中的消息,消息发布者可以发布消息到路径以STOCKS开头的主题中.
Guests can only consume from the STOCKS.ORCL topic
guests群组中的用户只能消费STOCKS.ORCL主题中的消息.
The previous example uses an additive model, where all operations on a topic have
been restricted to administrators only. Beyond this, specific operations on specific destinations
are added as needed.
前面例子中的配置采用了一种特殊的模式使得所有的主题操作都受到管理员的约束.除此之外,还给
特定的消息目的地配置了具体的操作权限.
In order to start the broker to test out both the JAAS authentication plug-in as well
as the authorization entries, use the following command to start the broker:
为了测试配置了授权实体的JAAS插件,使用如下命令来启动代理:
${ACTIVEMQ_HOME}/bin/activemq console -Djava.security.auth.login.config=src/main/resources/org/apache/activemq/book/ch6/login.config xbean:src/main/resources/org/apache/activemq/book/ch6/activemq-authorization.xml
(window启动命令:%ACTIVEMQ_HOME%/bin/activemq -Djava.security.auth.login.config=src/main/resources/org/apache/activemq/book/ch6/login.config xbean:src/main/resources/org/apache/activemq/book/ch6/activemq-authorization.xml)
...
xbean:src/main/resources/org/apache/activemq/book/ch6/activemq-authorization.xml
INFO | PListStore:/Users/bsnyder/amq/apache-activemq-5.4.1/data/localhost/tmp_storage started
INFO | Using Persistence Adapter: KahaDBPersistenceAdapter[/Users/bsnyder/amq/apache-activemq-5.4.1/data/localhost/KahaDB]
INFO | JMX consoles can connect to service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi
INFO | ActiveMQ 5.4.1 JMS Message Broker (localhost) is starting
INFO | For help or more information please see:http://activemq.apache.org/
INFO | Scheduler using directory:/Users/bsnyder/amq/apache-activemq-5.4.1/data/localhost/scheduler
INFO | JobSchedulerStore:/Users/bsnyder/amq/apache-activemq-5.4.1/data/localhost/schedulerstarted
INFO | Listening for connections at: tcp://localhost:61616
INFO | Connector openwire Started
INFO | ActiveMQ JMS Message Broker
(localhost, ID:mongoose.local-62861-1289968271876-0:0) started
Note the use of the java.security.auth.login.config system property to point to
the login.config file. This ensures that ActiveMQ can locate the file for its use.
Now let’s see how introduction of authorization affects JMS clients. We’ll demonstrate
our authorization setup by trying to consume from the stock topics. As we were
doing for the publisher example in the previous section, we’ll modify our original
stock portfolio consumer and make it pass an appropriate connection username and
password. For example, in order to try consuming from the STOCKS.ORCL topic as
guest, we should add the following to the consumer (marked as bold):
注意,使用java.security.auth.login.config系统属性是为了指到login.config文件,保证
ActiveMQ能够定位并使用login.config文件.下面让我们看看引入的授权是如何影响客户端的.
我们将通过消费股票消息来说明授权操作步骤.如前面章节中修改publisher一样,我们将修改
原先的stock portfolio实例中consumer的代码以便能够传入适当的用户名和密码然后建立连接.
比如,为了使得guest用户可以消费来自STOCKS.ORCL主题的消息,我们需要添加下面的代码到consumer
(添加的代码以粗体显示)
...
private String username = "guest";
private String password = "password";
public Consumer() throws JMSException
{
factory = new ActiveMQConnectionFactory(brokerURL);
connection = factory.createConnection(username, password);
connection.start();
session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
}
...
Credentials have been added so that the consumer can create a connection to the broker
using an appropriate username and password. The modified consumer can be
found in the org.apache.activemq.book.ch6.Consumer class. Now we can run our
example and see how authorization configuration at the broker affects the client. First
start the publisher using the following command:
授权凭证被加入到consumer的代码中,这样consumer可以使用适当的用户名了密码来建立到代理
的连接.修改后的consumer参阅org.apache.activemq.book.ch6.Consumer类文件.现在,我们可以
运行修改后的例子,以便观察代理中采用的授权配置如何影响客户端.首先,使用下面的命令启动
publisher:
$ mvn exec:java -Dexec.mainClass=org.apache.activemq.book.ch6.Publisher -Dexec.args="CSCO ORCL"
...
Sending: {price=24.07337784180, stock=ORCL, offer=24.0974512196,up=true} on destination: topic://STOCKS.ORCL
Sending: {price=73.49647952723, stock=CSCO, offer=73.5699760067,up=false} on destination: topic://STOCKS.CSCO
Sending: {price=24.282731805343, stock=ORCL, offer=24.307014537149,up=true} on destination: topic://STOCKS.ORCL
Sending: {price=74.1916498091, stock=CSCO, offer=74.265841458,up=true} on destination: topic://STOCKS.CSCO
Sending: {price=24.350683304888, stock=ORCL, offer=24.375033988192,up=true} on destination: topic://STOCKS.ORCL
Sending: {price=24.46113711010, stock=ORCL, offer=24.485598247216,up=true} on destination: topic://STOCKS.ORCL
Sending: {price=24.219079287873, stock=ORCL, offer=24.243298367160,up=false} on destination: topic://STOCKS.ORCL
Sending: {price=24.282977831328, stock=ORCL, offer=24.307260809160,up=true} on destination: topic://STOCKS.ORCL
Sending: {price=24.33344653108, stock=ORCL, offer=24.35777997761,up=true} on destination: topic://STOCKS.ORCL
Sending: {price=73.86498266780, stock=CSCO, offer=73.93884765047,up=false} on destination: topic://STOCKS.CSCO
Published '10' of '10' price messages
...
Now let’s see what happens when we try to access different destinations with guest user
credentials. For example, if you instruct it to consume messages from STOCKS.CSCO
topic, you’ll see the following exception:
接下来,让我们看看使用guest用来访问不同的消息目的地时会发生什么情况.比如,假如使用下面的命令,使用guest用户
消费来自STOCKS.CSCO主题的消息,你将看到后面的异常:
$ mvn exec:java -Dexec.mainClass=org.apache.activemq.book.ch6.Consumer -Dexec.args="STOCKS.CSCO"
...
Exception in thread "main"
javax.jms.JMSException: User guest is not authorized to read from: topic://STOCKS.CSCO ...
This is exactly what we expected to happen. Consuming from the STOCKS.CSCO topic
is restricted due to the authorization settings in listing 6.2. But the authorization configuration
does allow guests to consume from the STOCKS.ORCL topic as shown in the
following example:
这个与我们期望的相符合,因为在代码列表6.2中对消费STOCKS.CSCO主题中的消息消费者做了限制.但是,授权配置中
运行guests群组中的用户消费来自STOCKS.ORCL主题中的消息,如下所示:
$ mvn exec:java -Dexec.mainClass=org.apache.activemq.book.ch6.Consumer -Dexec.args="STOCKS.ORCL"
(译注: 貌似-Dexec.args参数值应该为 -Dexec.args="ORCL" 否则出错,因为配置有权限的topic为STOCKS.ORCL,
代码中使用这个参数语句为:createTopic("STOCKS." + stock);
修改后的命令为: mvn exec:java -Dexec.mainClass=org.apache.activemq.book.ch6.Consumer -Dexec.args="ORCL")
...
ORCL 9.66 9.67 down
ORCL 9.70 9.71 up
ORCL 9.80 9.81 up
ORCL 9.83 9.84 up
ORCL 9.80 9.81 down
ORCL 9.75 9.76 down
ORCL 9.81 9.82 up
ORCL 9.88 9.89 up
ORCL 9.80 9.81 down
ORCL 9.84 9.85 up
ORCL 9.84 9.85 up
ORCL 9.86 9.87 up
ORCL 9.95 9.96 up
ORCL 10.03 10.04 up
ORCL 10.03 10.04 down
...
As you can see, the authorization settings allowed only read access to the STOCKS.ORCL
topic for users that belong to the guests group.
正如你看到的那样,授权设置只允许来自guests群组的用户读取来自STOCKS.ORCL主题的消息.
These simple examples demonstrate how easy it is to secure ActiveMQ destinations
and assign different security levels to various users and groups. But what if defining
the access levels per destination isn’t enough for your application’s needs? Luckily,
ActiveMQ allows you to do a message-based authorization as well.
这些简单的例子说明,对ActiveMQ进行安全控制并为不同的群组和用户设置不同的安全等级是相当容易的.
但是,假如在消息目的地级别进行访问控制已不能完全符合应用需求时,改如何操作? 很幸运的是,ActiveMQ
还允许你进行基于消息级别的授权.
6.2.2 Message-level authorization
6.2.2 消息级别的授权
So far in this chapter, we’ve covered broker-level authentication and authorization.
But as you can see, authorization was granted or denied in the process of creating a
connection to the broker. In some situations you might want to authorize access to
only particular messages in a destination. In this section, we’ll examine such message
level authorization.
到目前为止,我们已经介绍了代理界别的认证和授权.但是,正如你所看到的,授权被允许或拒绝的过程都是
发生在创建到代理的连接时.有些情况下,你可能需要授权仅仅针对某个消息目的地中的某个消息.本节中
我们将讨论这种消息级别的授权.
We’ll implement a simple authorization plug-in that allows only applications running
on the same host as the broker (the localhost) to consume messages. The first
thing we need to do is to create an implementation of the org.apache.activemq.
security.MessageAuthorizationPolicy interface, as shown in the following listing.
我们将实现一个简单授权插件,该插件仅允许与代理(localhost)在同一机器上的应用程序消费消息.
首先我们要做的就是实现org.apache.activemq.security.MessageAuthorizationPolicy的接口,
如下所示.
Listing 6.3 Implementation of MessageAuthorizationPolicy interface
代码清单6.3 实现MessageAuthorizationPolicy接口
public class AuthorizationPolicy implements MessageAuthorizationPolicy
{
private static final Log LOG = LogFactory.getLog(AuthorizationPolicy.class);
public boolean isAllowedToConsume(ConnectionContext context,Message message)
{
LOG.info(context.getConnection().getRemoteAddress());
String remoteAddress = context.getConnection().getRemoteAddress();
if (remoteAddress.startsWith("/127.0.0.1"))
{
LOG.info("Permission to consume granted");
return true;
}
else
{
LOG.info("Permission to consume denied");
return false;
}
}
}
As you can see, the MessageAuthorizationPolicy interface is simple and defines only
one method named isAllowedToConsume(). This method has access to the message
in question and the context of the connection in which the message will be consumed.
如上述代码所示,MessageAuthorizationPolicy接口很简单只有一个方法isAllowedToConsume().这个方法
可以访问需要权限控制的消息以及消息上下文中的连接(通过该连接,消息将会被消费).
In this example, the remote address property for a connection is used (via the
call to the Connection.getRemoteAddress() method) to distinguish a remote consumer
from a local consumer. The isAllowedToConsume() method then determines
whether the read operation is allowed for the given consumer. Of course, this implementation
is arbitrary. You can use any message property or even some message content
to make the determination. The implementation of this method is meant to be a
simple example.
在这个示例代码中,使用connection的远程地址属性值(通过Connection.getRemoteAddress()方法获取)来区分
远程的消息消费者是否是一个本地的消费者.接着,isAllowedToConsume()方法决定是否允许当前的消息消费者
进行读取消息的操作.当然,这个实现有些随意,你可以使用消息的任何属性甚至消息内容来决定当前消费者
能否消费该消息.这里的实现仅仅作为一个简单的示例.
Now this policy must be installed and configured in the ActiveMQ broker. The first
and most obvious step is to compile this class and package it in an appropriate JAR.
Place this JAR into the lib/ directory of the ActiveMQ distribution and the policy is
ready to be used. You can do that by building and copying the book examples JAR:
接下来,这个消息访问控制策略必须要安装并配置到ActiveMQ的代理中.首先,显而易见的步骤是编译这个类并
打包为适当的Jar文件.然后将改Jar文件放到ActiveMQ得到lib目录下面.你可以使用下面的命令编译并拷贝实例
Jar包到ActiveMQ得到lib目录下面:
$ mvn clean install
...
$ cp target/activemq-in-action-examples.jar ${ACTIVEMQ_HOME}/lib/
Second, the policy must be configured to create an instance of the Authorization-
Policy class in the ActiveMQ XML configuration file. Using the Spring beans–style
XML inside the <messageAuthorizationPolicy> element, the AuthorizationPolicy
class is instantiated when the broker starts up. Here’s an example of this configuration:
接着,必须在ActiveMQ的XML配置文件中配置策略并创建一个AuthorizationPolicy类的引用.使用Spring的
bean风格的配置在<messageAuthorizationPolicy>元素中配置一个bean,是的AuthorizationPolicy类在代理启动时
能够被实例化.下面是配置的实例代码:
...
<messageAuthorizationPolicy>
<bean class="org.apache.activemq.book.ch6.AuthorizationPolicy" xmlns="http://www.springframework.org/schema/beans" />
</messageAuthorizationPolicy>
...
The only step left is to start up ActiveMQ and test out the new policy. Here’s the command
to start up the broker using the appropriate configuration file:
接着需要启动ActiveMQ以便测试新的授权策略.下面是以相应的配置文件启动代理的命令:
${ACTIVEMQ_HOME}/bin/activemq console xbean:src/main/resources/org/apache/activemq/book/ch6/activemq-policy.xml
(window下命令:%ACTIVEMQ_HOME%/bin/activemq xbean:src/main/resources/org/apache/activemq/book/ch6/activemq-policy.xml)
...
Loading message broker from:
xbean:src/main/resources/org/apache/activemq/book/ch6/activemq-policy.xml
...
22:19:23,532 | INFO | PListStore:/Users/bsnyder/amq/apache-activemq-5.4.1/data/localhost/tmp_storage started
22:19:23,692 | INFO | JMX consoles can connect to service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi
22:19:23,717 | INFO | Using Persistence Adapter: KahaDBPersistenceAdapter[/Users/bsnyder/amq/apache-activemq-5.4.1/data/localhost/KahaDB]
22:19:23,815 | DEBUG | loading
22:19:23,847 | INFO | ActiveMQ 5.4.1 JMS Message Broker (localhost) is starting
22:19:23,848 | INFO | For help or more information please see: http://activemq.apache.org/
22:19:23,990 | INFO | Scheduler using directory:/Users/bsnyder/amq/apache-activemq-5.4.1/data/localhost/scheduler
22:19:24,037 | DEBUG | loading
22:19:24,039 | DEBUG | loading
22:19:24,041 | INFO | JobSchedulerStore:/Users/bsnyder/amq/apache-activemq-5.4.1/data/localhost/scheduler started
22:19:24,081 | INFO | Listening for connections at: tcp://localhost:61616
22:19:24,081 | INFO | Connector openwire Started
22:19:24,083 | INFO | ActiveMQ JMS Message Broker(localhost, ID:mongoose.local-64256-1289971163870-0:0) started
...
If you run the examples from chapter 3 now on the host on which your broker is running,
you’ll see that everything works in the same manner as it did with the original
configuration. The producer produces messages:
如果你在运行代理的机器上运行第3章中的例子,你会发现所有程序工作正常,就像使用前面的配置一样.
producer发布消息的命如下:
$ mvn exec:java -Dexec.mainClass=org.apache.activemq.book.ch3.portfolio.Publisher -Dexec.args="CSCO ORCL"
...
Sending: {price=94.51516220513759, stock=ORCL, offer=94.60967736734271,up=true} on destination: topic://STOCKS.ORCL
Sending: {price=94.12582896629408, stock=ORCL, offer=94.21995479526036,up=false} on destination: topic://STOCKS.ORCL
Sending: {price=52.82279394171494, stock=CSCO, offer=52.87561673565665,up=false} on destination: topic://STOCKS.CSCO
Sending: {price=93.30370880341836, stock=ORCL, offer=93.39701251222176,up=false} on destination: topic://STOCKS.ORCL
Sending: {price=94.0890269658999, stock=ORCL, offer=94.1831159928658,up=true} on destination: topic://STOCKS.ORCL
Sending: {price=52.50790406130471, stock=CSCO, offer=52.56041196536601,up=false} on destination: topic://STOCKS.CSCO
Sending: {price=94.11072880595002, stock=ORCL, offer=94.20483953475596,up=true} on destination: topic://STOCKS.ORCL
Sending: {price=52.947263764976896, stock=CSCO, offer=53.000211028741866,up=true} on destination: topic://STOCKS.CSCO
Sending: {price=94.40912590172766, stock=ORCL, offer=94.50353502762938,up=true} on destination: topic://STOCKS.ORCL
Sending: {price=95.0802935408136, stock=ORCL, offer=95.1753738343544,up=true} on destination: topic://STOCKS.ORCL
Published '10' of '10' price messages
And the consumer receives these stock messages:
consumer接收消息命令如下:
$ mvn exec:java -Dexec.mainClass=org.apache.activemq.book.ch3.portfolio.Consumer -Dexec.args="CSCO ORCL"
...
ORCL 94.52 94.61 up
ORCL 94.13 94.22 down
CSCO 52.82 52.88 down
ORCL 93.30 93.40 down
ORCL 94.09 94.18 up
CSCO 52.51 52.56 down
ORCL 94.11 94.20 up
CSCO 52.95 53.00 up
ORCL 94.41 94.50 up
ORCL 95.08 95.18 up
CSCO 52.90 52.96 down
ORCL 95.62 95.71 up
CSCO 53.32 53.37 up
ORCL 95.45 95.55 down
CSCO 53.59 53.64 up
...
You can also notice log messages from the policy in the broker’s console:
你可以看到运行代理的控制台窗口会打印以下信息:
INFO | /127.0.0.1:50930
INFO | Permission to consume granted
INFO | /127.0.0.1:50930
INFO | Permission to consume granted
INFO | /127.0.0.1:50930
INFO | Permission to consume granted
INFO | /127.0.0.1:50930
INFO | Permission to consume granted
INFO | /127.0.0.1:50930
INFO | Permission to consume granted
INFO | /127.0.0.1:50930
INFO | Permission to consume granted
But, when run from another host (for example, 192.168.10.10), the consumer won’t
be able to consume messages, as our policy will deny the access. And you’ll notice log
messages similar to these in the broker’s console:
但是,假如在其他机器上(比如在192.168.10.10)运行consumer,consumer将不能消费消息,因为前面
的策略拒绝了consumer访问消息.你将在运行consumer的控制台上看到类型下面的日志信息:
INFO | /192.168.10.10:50930
INFO | Permission to consume denied
INFO | /192.168.10.10:50930
INFO | Permission to consume denied
INFO | /192.168.10.10:50930
INFO | Permission to consume denied
INFO | /192.168.10.10:50930
INFO | Permission to consume denied
INFO | /192.168.10.10:50930
INFO | Permission to consume denied
INFO | /192.168.10.10:50930
INFO | Permission to consume denied
In this way, we verified that our message-based policy works and enables message consumption
only from the local host.
通过这种方式,我们验证了基于消息的安全策略能够正常工作并且保证只有来自本地机器的消息
消费者能消费消息.
Message-level authorization provides some powerful functionality with endless possibilities.
Although a simple example was used here, you can adapt it to any security
mechanism used in your project. Just bear in mind that a message authorization policy
is executed for every message that flows through the broker. So be careful not to
add functionality that could possibly slow down the flow of messages.
消息级的授权提供了一些强大的功能与无限的可能性。尽管这里使用的仅仅是一个简单的示例,但你可以
将消息级别的授权扩展使用任意安全机制的工程中.需要注意的是消息授权策略将影响该代理中的所有消息.
所以,需要注意不要因为增加消息验证的功能额拖慢了消息的传送.
In addition to authorization, ActiveMQ provides a special class for tighter control
over broker-level operations that’s even more powerful. The next section examines
and demonstrates just such an example.
除了授权机制外,ActiveMQ还提供一种特殊的类以便能够更紧密的控制代理级别的操作,进而提供
更加强大的功能.下面的章节中,我们将以一个实例来说明.