zoukankan      html  css  js  c++  java
  • ActiveMQ的安全机制使用及其源代码分析 [转]

    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 arget下生成二进制分发包。若下载的是二进制分发包,解压即可。

    ActiveMQ的二进制分发包目录如下所示:

    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原有安全机制均基于插件实现,实现思路如图所示。

    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的验证和授权。

  • 相关阅读:
    数据分析系统DIY1/3:CentOS7+MariaDB安装纪实
    NSArray与NSString、NSData,NSDictionary与NSString、NSData 相互转化
    Geek地生活,文艺地思考
    Android开发中遇到的问题(五)——Eclipse导入Android项目出现"Invalid project description overlaps the location of another project"错误的解决办法
    Android开发中遇到的问题(四)——Android中WARNING: Application does not specify an API level requirement!的解决方法
    Android开发中遇到的问题(三)——eclipse创建android项目无法正常预览布局文件
    Android开发中遇到的问题(二)——新建android工程的时候eclipse没有生成MainActivity和layout布局
    Android开发学习总结(三)——appcompat_v7项目说明
    Android开发学习总结(二)——使用Android Studio搭建Android集成开发环境
    Android开发学习总结(一)——搭建最新版本的Android开发环境
  • 原文地址:https://www.cnblogs.com/jimcsharp/p/5617842.html
Copyright © 2011-2022 走看看