ActiveMQ是目前较为流行的一款开源消息服务器。最近在项目开发中,需要为ActiveMQ开发基于IP的验证和授权机制,因此,对ActiveMQ的安全机制进行了了解,以下将介绍ActiveMQ的安全机制使用及其源代码分析。
本文开发环境介绍:
操作系统:Windows XP
Java:jdk 1.6.0_12
maven:maven 3.0.4
ActiveMQ:ActiveMQ 5.6.0
ActiveMQ安全机制的介绍
安 全机制一般包含验证(Authentication)和授权(Authorization)两部分。在ActiveMQ中,验证指通过访问者的用户名和密 码实现用户身份的验证,授权指为消息目标(队列或主题)的读、写、管理指定具有相应权限的用户组,并为用户分配权限。ActiveMQ的安全机制基于插件 实现。
ActiveMQ提供两种验证插件,分别是:
1)Simple authentication plugin-in;
2)JAAS(Java Authentication and Authorization Service)authentication plugin-in。ActiveMQ提供一种授权插件:Authorization plugin-in。
ActiveMQ安全机制的使用
1. ActiveMQ的使用
可 从ActiveMQ官网“http://activemq.apache.org/”下载ActiveMQ的源代码包或二进制分发包。由于 ActiveMQ使用Java开发,因此需要预先安装jdk,另外,由于ActiveMQ的开发使用了maven,因此,若下载的是源代码包,需要预先安 装maven。解压源代码包,并在源代码包目录下执行“mvn install -Dmaven.test.skip=true ”完成编译、打包和安装,成功后,会在assembly\target下生成二进制分发包。若下载的是二进制分发包,解压即可。
ActiveMQ的二进制分发包目录如下所示:

进入bin文件,执行脚本,即可运行ActiveMQ。
2. Simple authentication plugin-in的使用
在activemq.xml中如下配置:
<plugins> <simpleAuthenticationPlugin> <users> <authenticationUser username="system" password="password" groups="users,admins"/> <authenticationUser username="user" password="password" groups="users"/> <authenticationUser username="guest" password="password" groups="guests"/> </users> </simpleAuthenticationPlugin> </plugins>
|
3. JAAS authentication plugin-in的使用
在activemq.xml中如下配置:
<plugins> <jaasAuthenticationPlugin configuration="activemq-domain" /> </plugins>
|
创建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"; };
|
创建users.properties和groups.properties文件,包含用户和用户组信息。
users.properties:
system=password user=password guest=password
|
groups.properties:
admins=system users=system,user guests=guest
|
4. Authorization plugin-in的使用
在activemq.xml中如下配置:
<plugins> <authorizationPlugin> <map> <authorizationMap> <authorizationEntries> <authorizationEntry queue=">" read="admins" write="admins" admin="admins" /> <authorizationEntry queue="USERS.>" read="users" write="users" admin="users" /> <authorizationEntry queue="GUEST.>" read="guests" write="guests,users" admin="guests,users" />
<authorizationEntry queue="TEST.Q" read="guests" write="guests" />
<authorizationEntry topic=">" read="admins" write="admins" admin="admins" /> <authorizationEntry topic="USERS.>" read="users" write="users" admin="users" /> <authorizationEntry topic="GUEST.>" read="guests" write="guests,users" admin="guests,users" />
<authorizationEntry topic="ActiveMQ.Advisory.>" read="guests,users" write="guests,users" admin="guests,users"/> </authorizationEntries> </authorizationMap> </map> </authorizationPlugin> </plugins>
|
ActiveMQ安全机制的源代码分析
ActiveMQ在其maven工程的activemq-core模块中实现安全机制。ActiveMQ原有安全机制均基于插件实现,实现思路如图所示。

其中,Broker接口是ActiveMQ的核心接口,ActiveMQ消息服务器对象即该接口的实现。接口BrokerPlugin通过installPlugin方法传入Broker对象,为其创建插件。BrokerFilter类也实现自Broker接口,其与Broker的关系,类似于Struts中Interceptor与Action的关系,多个BrokerFilter对象以及消息服务器Broker对象通过指向下一个对象的引用next构成链状结构,当创建连接、消息生产者、消息消费者时,先后执行BrokerFilter中的相应方法,直至执行消息服务器中的方法,而安全机制类即继承自BrokerFilter。
ActiveMQ原有安全机制的相关类均继承或实现自上述类或接口,安全机制的类包为activemq-core中的org.apache.activemq.security。
1. Simple authentication plugin-in的源代码分析
Simple authentication plugin-in主要包含两个基本类:SimpleAuthenticationPlugin(实现自BrokerPlugin)和SimpleAuthenticationBroker(继承自BrokerFilter)。
SimpleAuthenticationPlugin部分代码:
public class SimpleAuthenticationPlugin implements BrokerPlugin { private Map<String, String> userPasswords; private Map<String, Set<Principal>> userGroups; private static final String DEFAULT_ANONYMOUS_USER = "anonymous"; private static final String DEFAULT_ANONYMOUS_GROUP = "anonymous"; private String anonymousUser = DEFAULT_ANONYMOUS_USER; private String anonymousGroup = DEFAULT_ANONYMOUS_GROUP; private boolean anonymousAccessAllowed = false; //...... //安装插件时,根据activemq.xml中的配置,新建 SimpleAuthenticationBroker对象, 并返回该对象 public Broker installPlugin(Broker parent) { SimpleAuthenticationBroker broker = new SimpleAuthenticationBroker(parent, userPasswords, userGroups); broker.setAnonymousAccessAllowed(anonymousAccessAllowed); broker.setAnonymousUser(anonymousUser); broker.setAnonymousGroup(anonymousGroup); return broker; } //...... }
|
SimpleAuthenticationBroker部分代码:
public class SimpleAuthenticationBroker extends BrokerFilter {
private boolean anonymousAccessAllowed = false; private String anonymousUser; private String anonymousGroup; private final Map<String,String> userPasswords; private final Map<String,Set<Principal>> userGroups; private final CopyOnWriteArrayList<SecurityContext> securityContexts = new CopyOnWriteArrayList<SecurityContext>();
//...... //由于验证需要在创建连接时进行,因此重写BrokerFilter的addConnection方法 public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {
SecurityContext s = context.getSecurityContext(); if (s == null) {
//若允许匿名访问,则不进行验证 // Check the username and password. if (anonymousAccessAllowed && info.getUserName() == null && info.getPassword() == null) { info.setUserName(anonymousUser); s = new SecurityContext(info.getUserName()) { public Set<Principal> getPrincipals() { Set<Principal> groups = new HashSet<Principal>(); groups.add(new GroupPrincipal(anonymousGroup)); return groups; } }; //若不允许匿名访问,则验证连接的用户名和密码是否与配置文件中的一致,若不一致,则抛出安全异常 } else { String pw = userPasswords.get(info.getUserName()); if (pw == null || !pw.equals(info.getPassword())) { throw new SecurityException( "User name [" + info.getUserName() + "] or password is invalid."); }
final Set<Principal> groups = userGroups.get(info.getUserName()); s = new SecurityContext(info.getUserName()) { public Set<Principal> getPrincipals() { return groups; } }; }
context.setSecurityContext(s); securityContexts.add(s); }
//调用父对象的addConnection方法,即调用next引用的Broker对象的addConnection方法 try { super.addConnection(context, info); } catch (Exception e) { securityContexts.remove(s); context.setSecurityContext(null); throw e; } } //...... }
|
2. JAAS authentication plugin-in的源代码分析
JAAS authentication plugin-in主要包含两个基本类:JaasAuthenticationPlugin(实现自BrokerPlugin)JaasAuthenticationBroker(继承自BrokerFilter)。
JaasAuthenticationPlugin部分代码:
public class JaasAuthenticationPlugin implements BrokerPlugin { protected String configuration = "activemq-domain"; //...... public Broker installPlugin(Broker broker) { //读取配置文件, 初始化JAAS initialiseJaas(); //创建JaasAuthenticationBroker对象并返回 return new JaasAuthenticationBroker(broker, configuration); } //...... }
|
JaasAuthenticationBroker部分代码:
public class JaasAuthenticationBroker extends BrokerFilter {
private final String jassConfiguration; private final CopyOnWriteArrayList<SecurityContext> securityContexts = new CopyOnWriteArrayList<SecurityContext>();
//...... //由于验证需要在创建连接时进行,因此重写BrokerFilter的addConnection方法 public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {
if (context.getSecurityContext() == null) { // Set the TCCL since it seems JAAS needs it to find the login // module classes. ClassLoader original = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(JaasAuthenticationBroker.class.getClassLoader()); try { // Do the login. try { JassCredentialCallbackHandler callback = new JassCredentialCallbackHandler(info .getUserName(), info.getPassword()); LoginContext lc = new LoginContext(jassConfiguration, callback); lc.login(); Subject subject = lc.getSubject();
//基于JAAS判断用户名和密码是否正确 SecurityContext s = new JaasSecurityContext(info.getUserName(), subject); context.setSecurityContext(s); securityContexts.add(s); } catch (Exception e) { throw (SecurityException)new SecurityException("User name [" + info.getUserName() + "] or password is invalid.") .initCause(e); } } finally { Thread.currentThread().setContextClassLoader(original); } } //调用父对象的addConnection方法,即调用next引用的Broker对象的addConnection方法 super.addConnection(context, info); } //...... }
|
3. Authorization plugin-in的源代码分析
Authorization plugin-in主要包含两个基本类:AuthorizationPlugin(实现自BrokerPlugin)AuthorizationBroker(继承自BrokerFilter)。
AuthorizationPlugin部分代码:
ublic class AuthorizationPlugin implements BrokerPlugin {
//AuthorizationMap对象存储activemq.xml中消息目标、读、写、管理用户组信息 private AuthorizationMap map;
//...... //创建 AuthorizationBroker 对象并返回 public Broker installPlugin(Broker broker) { if (map == null) { throw new IllegalArgumentException("You must configure a 'map' property"); } return new AuthorizationBroker(broker, map); } //...... }
|
AuthorizationBroker部分代码:
public class AuthorizationBroker extends BrokerFilter implements SecurityAdminMBean {
private final AuthorizationMap authorizationMap;
//...... //由于需要授权是否可管理消息目标,因此重写BrokerFilter的 addDestinationInfo 方法 @Override public void addDestinationInfo(ConnectionContext context, DestinationInfo info) throws Exception { addDestination(context, info.getDestination(),true); super.addDestinationInfo(context, info); }
//由于需要授权是否可管理消息目标,因此重写BrokerFilter的 addDestination 方法 @Override public Destination addDestination(ConnectionContext context,
ActiveMQDestination destination,boolean create) throws Exception { final SecurityContext securityContext = context.getSecurityContext(); if (securityContext == null) { throw new SecurityException("User is not authenticated."); }
Destination existing = this.getDestinationMap().get(destination); if (existing != null) { return super.addDestination(context, destination,create); }
//从访问控制列表中查看是否具有授权 if (!securityContext.isBrokerContext()) { Set<?> allowedACLs = null; if (!destination.isTemporary()) { allowedACLs = authorizationMap.getAdminACLs(destination); } else { allowedACLs = authorizationMap.getTempDestinationAdminACLs(); }
if (allowedACLs != null && !securityContext.isInOneOf(allowedACLs)) { throw new SecurityException("User " + securityContext.getUserName() + " is not authorized to create: " + destination); }
}
//调用next引用的addDestination方法 return super.addDestination(context, destination,create); }
//由于需要授权是否可读消息,因此重写BrokerFilter的 addConsume
r 方法,在该方法中,从访问控制列表中查看是否具有读授权,并调用next引用的addConsumer方法 @Override public Subscription addConsumer(ConnectionContext context, ConsumerInfo info) throws Exception { //...... return super.addConsumer(context, info); }
//由于需要授权是否可写消息,因此重写BrokerFilter的 addProducer 方法,在该方法中,
从访问控制列表中查看是否具有写授权,并调用next引用的 addProducer 方法 @Override public void addProducer(ConnectionContext context, ProducerInfo info) throws Exception { //...... super.addProducer(context, info); } //...... }
|
ActiveMQ提供了便利的插件开发方式,并基于插件实现了包含验证和授权的安全机制。参考ActiveMQ的源代码,可以进行插件开发,实现个性化的安全机制,如基于IP的验证和授权。
- hostapd源代码分析(二):hostapd的工作机制
[转]hostapd源代码分析(二):hostapd的工作机制 原文链接:http://blog.csdn.net/qq_21949217/article/details/46004433 在我的上一 ...
- Android 中View的绘制机制源代码分析 三
到眼下为止,measure过程已经解说完了,今天開始我们就来学习layout过程.只是在学习layout过程之前.大家有没有发现我换了编辑器,哈哈.最终下定决心从Html编辑器切换为markdown编 ...
- mybatis源代码分析:mybatis延迟加载机制改进
在上一篇博客<mybatis源代码分析:深入了解mybatis延迟加载机制>讲诉了mybatis延迟加载的具体机制及实现原理. 可以看出,如果查询结果对象中有一个属性是需要延迟加载的,那整 ...
- Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6642463 在前面几篇文章中,我们详细介绍了A ...
- Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6633311 在上一篇文章中,我 们分析了And ...
- Android应用Activity、Dialog、PopWindow、Toast窗体加入机制及源代码分析
[工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处.尊重劳动成果] 1 背景 之所以写这一篇博客的原因是由于之前有写过一篇<Android应用setCont ...
- cocos2d-x 源代码分析 : EventDispatcher、EventListener、Event 源代码分析 (新触摸机制,新的NotificationCenter机制)
源代码版本号来自3.x,转载请注明 cocos2d-x 源代码分析总文件夹 http://blog.csdn.net/u011225840/article/details/31743129 1.继承结 ...
- Android 中View的绘制机制源代码分析 一
尊重原创: http://blog.csdn.net/yuanzeyao/article/details/46765113 差点儿相同半年没有写博客了,一是由于工作比較忙,二是认为没有什么内容值得写, ...
- nginx源代码分析--进程间通信机制 & 同步机制
Nginx源代码分析-进程间通信机制 从nginx的进程模型能够知道.master进程和worker进程须要通信,nginx中通信的方式有套接字.共享内存.信号.对于master进程,从外部接受信号, ...
随机推荐
- jquery改变多个css样式
<!DOCTYPE html> <html> <head> <script src="http://ajax.googleapis.com/ajax ...
- CentOS 最小化安装后安装桌面
通过yum的方式安装: yum groupinstall -y "Desktop" "Desktop Platform" "Desktop ...
- PythonCrawl自学日志(4)
2016年9月22日10:34:02一.Selector1.如何构建(1)text构建: body = '<html><body><span>good</sp ...
- testlink的下载地址
http://sourceforge.jp/projects/sfnet_testlink/downloads/TestLink%201.9/TestLink%201.9.12/testlink-1. ...
- python函数的返回值 讲解
我们一起来聊聊python函数返回值的特殊情况,之前我也碰到过类似方面的问题,到后来查阅了一些资料后,发现原来是这样. 首先,写函数的时候,一定要写函数的文档,这样方便我们识别函数是做什么的.我记得很 ...
- python中 “与,或,异或”
在python编程语言里面: 按位的运算,都按位的运算,都是把参加运算的数的二进制形式进行运算. 1.与运算:A与B值均为1时,A.B与的运算结果才为1,否则为0 (运算符:&) 2.或运算: ...
- hdu 3549 Flow Problem Edmonds_Karp算法求解最大流
Flow Problem 题意:N个顶点M条边,(2 <= N <= 15, 0 <= M <= 1000)问从1到N的最大流量为多少? 分析:直接使用Edmonds_Karp ...
- Convert.ToString和ToString的区别
Convert.ToString能处理字符串为null的情况,不抛出异常. ToString方法不能处理字符串为null的情况,会抛出异常.如:“未将对象引用设置到对象的实例”.
- 认识基本的UI资源
什么是UI精灵(Sprite) 在制作UI时,经常将一些零碎的小的UI资源(比如,一个小箭头,一个按钮等)打包成一张大图,然后在使用时,只使用这个大图中的一部分,那么这一块"被切出来&quo ...
- 使用自定义 URL 实现控制器之间的跳转-b
一个app往往有很多界面,而界面之间的跳转也就是对应控制器的跳转,控制器的跳转一般有两种情况 push 或者 modal,push 和 modal 的默认效果是系统提供的 文章配图 1. 概述 系统提 ...