1. 程式人生 > >activemq自定義安全驗證外掛,整合mysql

activemq自定義安全驗證外掛,整合mysql

一.下載外掛程式碼所需jar包,我是在maven中引入,然後再copy到activemq的指定目錄:

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.apache.activemq/activemq-broker -->
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-broker</artifactId>
            <version>5.15.8</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>6.0.6</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>4.3.18.RELEASE</version>
        </dependency>
    </dependencies>

將mysql-connector-java-6.0.6.jar包copy到C:\apache-activemq-5.15.8\lib目錄下,將spring-jdbc-4.3.18.RELEASE.jar包copy到C:\apache-activemq-5.15.8\lib\optional目錄下。注意:spring-jdbc版本要和C:\apache-activemq-5.15.8\lib\optional目錄下的spring版本相同。

二.編輯C:\apache-activemq-5.15.8\conf下的activemq.xml,<bean>節點配置改為:

<!-- Allows us to use system properties as variables in this configuration file -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
			<list>
                <value>file:${activemq.conf}/credentials.properties</value>
                <value>file:${activemq.conf}/db.properties</value>
            </list>      
        </property>		
    </bean>

   <!-- Allows accessing the server log -->
    <bean id="logQuery" class="io.fabric8.insight.log.log4j.Log4jLogQuery"
          lazy-init="false" scope="singleton"
          init-method="start" destroy-method="stop">
    </bean>

	<!-- mysql資料庫資料來源-->  
    <bean id="mySqlDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">  
            <property name="driverClassName" value="${jdbc.driverClassName}" />  
            <property name="url" value="${jdbc.url}" />  
            <property name="username" value="${jdbc.username}" />  
            <property name="password" value="${jdbc.password}" />  
    </bean>  
    <!-- 增加jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" abstract="false"
        lazy-init="false" autowire="default" >
        <property name="dataSource">
            <ref bean="mySqlDataSource" />
        </property>
    </bean>

然後在<broker>節點中的最後新增配置:

		<plugins>
            <bean xmlns="http://www.springframework.org/schema/beans" id="myPlugin" class="my.plugin.MyAuthenticationPlugin">						
                <constructor-arg>
                    <ref bean="jdbcTemplate"/>
                </constructor-arg>
            </bean>
        </plugins>

		
    </broker>

其中db.properties是自己新建的配置檔案,內容如下:

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/yzh?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false
jdbc.username=root
jdbc.password=123456

建立好後copy到C:\apache-activemq-5.15.8\conf目錄下。

這樣服務端的配置就完成了,下面是自定義外掛的程式碼。

三.認證外掛的編碼:

package my.plugin;

import org.apache.activemq.broker.Broker;
import org.apache.activemq.broker.BrokerPlugin;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.List;

/**
 * description:
 * author: 
 * date: 2018-12-07 09:54
 **/
public class MyAuthenticationPlugin implements BrokerPlugin {

    JdbcTemplate jdbcTemplate;//注入JdbcTemplate

    public MyAuthenticationPlugin(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public Broker installPlugin(Broker broker) throws Exception {
        return new MyAuthenticationBroker(broker,jdbcTemplate);
    }

}

其中MyAuthenticationBroker的編碼:

package my.plugin;

import org.apache.activemq.broker.Broker;
import org.apache.activemq.broker.BrokerFilter;
import org.apache.activemq.broker.ConnectionContext;
import org.apache.activemq.command.ConnectionInfo;
import org.apache.activemq.security.AbstractAuthenticationBroker;
import org.apache.activemq.security.SecurityContext;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.regex.Pattern;

/**
 * description:  broker的實現,附帶方法過濾
 * author: yangzihe
 * date: 2018-12-07 09:50
 **/
public class MyAuthenticationBroker extends AbstractAuthenticationBroker {

    private JdbcTemplate jdbcTemplate;

    public MyAuthenticationBroker(Broker next, JdbcTemplate jdbcTemplate) {
        super(next);
        this.jdbcTemplate = jdbcTemplate;
    }

    /**
     * 建立連線的時候攔截
     *
     * @param context
     * @param info
     * @throws Exception
     */
    @Override
    public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {
        SecurityContext securityContext = context.getSecurityContext();
        if (securityContext == null) {                       System.out.println("============================================username=" + info.getUserName() + ",password=" + info.getPassword());
            securityContext = authenticate(info.getUserName(), info.getPassword(), null);
            context.setSecurityContext(securityContext);
            securityContexts.add(securityContext);
        }

        try {
            super.addConnection(context, info);
        } catch (Exception e) {
            securityContexts.remove(securityContext);
            context.setSecurityContext(null);
            throw e;
        }
    }

    /**
     * 查詢資料庫獲取user,優化:載入時將資料庫所有user放入記憶體
     *
     * @param username
     * @return
     */
    private User getUser(String username) {
        String sql = "select * from db_user where username=? limit 1";
        try {
            User user = jdbcTemplate.queryForObject(sql, new Object[]{username}, new BeanPropertyRowMapper<User>(User.class));
            return user;
        } catch (EmptyResultDataAccessException e) {
            return null;
        }
    }

    public static Map<String, User> map = null;

    public void getAllUser() {
        String sql = "select * from db_user";
        List<User> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<User>(User.class));
        map = new LinkedHashMap<String, User>();
        for (User user : list) {
            System.err.println(user);//debug
            map.put(user.getUser_name(), user);
        }
    }

    /**
     * 認證
     *
     * @param username
     * @param password
     * @param peerCertificates
     * @return
     * @throws SecurityException
     */
    public SecurityContext authenticate(String username, String password, X509Certificate[] peerCertificates) throws SecurityException {
        SecurityContext securityContext = null;
//        User user = getUser(username);
        if (map == null || map.size() <= 0) {
            System.out.println("==============連線mysql=================");
            getAllUser();
        }
        User user = map.get(username);//記憶體中獲取
        //驗證使用者資訊
        if (user != null && user.getPass_word().equals(password)) {
            securityContext = new SecurityContext(username) {
                @Override
                public Set<Principal> getPrincipals() {
                    Set<Principal> groups = new HashSet<Principal>();
                    //groups.add(new GroupPrincipal("users"));//預設加入了users的組
                    return groups;
                }
            };
        } else {
            throw new SecurityException("my auth plugin authenticate failed");
        }
        return securityContext;
    }
}

其中User是資料庫表db_user的對映實體類:

package my.plugin;

/**
 * description:
 * author: yangzihe
 * date: 2018-12-07 16:25
 **/
public class User {

    private String user_name;

    private String pass_word;

    @Override
    public String toString() {
        return "User{" +
                "user_name='" + user_name + '\'' +
                ", pass_word='" + pass_word + '\'' +
                '}';
    }

    public User() {
    }

    public User(String user_name, String pass_word) {
        this.user_name = user_name;
        this.pass_word = pass_word;
    }

    public String getUser_name() {
        return user_name;
    }

    public void setUser_name(String user_name) {
        this.user_name = user_name;
    }

    public String getPass_word() {
        return pass_word;
    }

    public void setPass_word(String pass_word) {
        this.pass_word = pass_word;
    }
}

最後,資料庫表的sql:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for db_user
-- ----------------------------
DROP TABLE IF EXISTS `db_user`;
CREATE TABLE `db_user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
  `pass_word` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of db_user
-- ----------------------------
INSERT INTO `db_user` VALUES (1, 'admin', 'admin');

SET FOREIGN_KEY_CHECKS = 1;

四.activemq broker服務端配置修改和外掛程式碼的編寫都已完成,最後就是啟動activemq,cmd進入C:\apache-activemq-5.15.8\bin,執行activemq start,或者直接找對應的64位的activemq.bat執行檔案雙擊。服務啟動後,客戶端再次連線的時候,username=admin password=admin,如果不是將連線失敗。(在外掛程式碼中可自定義自己的驗證邏輯)