mqtt推送,Activemq 擴展插件

業務場景

工作中使用mqtt做推送,activemq作爲broker

問題

(1)認證。客戶端連接broker需要認證

(2)主題權限。客戶只能訂閱自己有權限的主題

方案

1 認證

基本原理是activemq支持自定義插件。

公司系統是前後分離的,我們用戶在登錄後,會將token存與redis。這個認證也是基於redis來認證。

直接上代碼:maven項目

1.1 依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.wyl</groupId>
    <artifactId>broker-filter</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <dependencies>
        <!-- activemq -->
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-broker</artifactId>
            <version>5.15.8</version>
        </dependency>

        <!-- jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>
    </dependencies>

</project>

1.2 插件入口類

注意,activemq支持注入的。詳見官方文檔:http://activemq.apache.org/developing-plugins.html。所以這裏的jedisPool,可以在配置文件中注入

package com.yiwo.plugin;

import org.apache.activemq.broker.Broker;
import org.apache.activemq.broker.BrokerPlugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.JedisPool;

/**
 * broker 過濾器
 *
 * @author wyl
 * @since 2018-12-5
 */
public class BrokerFilter implements BrokerPlugin {

    private Logger logger = LoggerFactory.getLogger(BrokerFilter.class);

    private JedisPool jedisPool;

    public Broker installPlugin(Broker broker) throws Exception {
        logger.info("=============plugin install");
        return new AuthFilter(broker, jedisPool);
    }

    public JedisPool getJedisPool() {
        return jedisPool;
    }

    public void setJedisPool(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }
}

1.3 認證過濾類

package com.yiwo.plugin;

import org.apache.activemq.broker.Broker;
import org.apache.activemq.broker.BrokerFilter;
import org.apache.activemq.broker.ConnectionContext;
import org.apache.activemq.broker.region.Subscription;
import org.apache.activemq.command.ConnectionInfo;
import org.apache.activemq.command.ConsumerInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.HashMap;
import java.util.Map;

/**
 * broker過濾器
 *
 * @author wyl
 * @since 2018-12-6
 */
public class AuthFilter extends BrokerFilter {

    private Logger logger = LoggerFactory.getLogger(AuthFilter.class);

    private JedisPool jedisPool;

    public AuthFilter(Broker next, JedisPool jedisPool) {
        super(next);
        this.jedisPool = jedisPool;
        logger.info("=============install authFilter");
    }

    @Override
    public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {

        long start = System.currentTimeMillis();
        logger.info("=============addConnection");
        logger.info("=============username=" + info.getUserName());
        logger.info("=============password=" + info.getPassword());

        auth(info.getUserName(), info.getPassword());

        long end = System.currentTimeMillis();
        logger.info("=============total time=" + (end - start));

        super.addConnection(context, info);
    }

    /**
     * 認證
     *
     * @param userName 前綴+用戶id。如 user:123
     * @param password 登錄token。如 48fe6bddc509444c8773760427c52c96
     * @return
     * @author wyl
     * @since 2018-12-6
     */
    private void auth(String userName, String password) {
        if (userName == null || password == null) {
            throw new SecurityException("Invalid  userName or password!");
        }

        String token = getUserRealToken(userName);
        if (!token.equals(password)) {
            throw new SecurityException("Invalid  userName or password!");
        }
    }

    /**
     * 獲取用戶的token
     *
     * @param username 前綴+用戶id。user:123
     * @return
     * @author wyl
     * @since 2018-12-6
     */
    private String getUserRealToken(String username) {
      
//        String token = jedisPool.getResource().get(username);
        Jedis resource = jedisPool.getResource();
        String token = resource.get(username);
        resource.close();

        logger.info("=============token=" + token);
        if (token == null) {
            throw new SecurityException("Invalid  userName or password!");
        }

        return token;
    }

 

坑:

剛開始使用:

String token = jedisPool.getResource().get(username);

獲取token。

web端訂閱可以成功,但是刷新多次之後就會連不上,經過排查,日誌

logger.info("=============token=" + token);

一直打印不出來,說明進入方法getUserRealToken()後有問題,方法中最有可能出問題的就是

String token = jedisPool.getResource().get(username);

猜想是jedisPool鏈接釋放問題,改爲:

Jedis resource = jedisPool.getResource();
String token = resource.get(username);
resource.close();

問題解決

1.4 配置插件

由於我們引入了redis的依賴,所以要將其jar包加入activemq的lib目錄。

另外activemq-broker已經有了,所有只需要將jedis-2.9.0.jar 和 我們的broker-filter-1.0-SNAPSHOT.jar 上傳即可

 

修改配置文件:activemq/conf/activemq.xml

首先是jedispool,配置在<beans>標籤中

<bean id="jedispool"  class="redis.clients.jedis.JedisPool">
  <constructor-arg name="host" value="192.168.0.110"></constructor-arg>
  <constructor-arg name="port" value="6379"></constructor-arg>
</bean>

然後是我們的插件,要配置在<broker>中,並注入jedispool

<plugins>
  <bean xmlns="http://www.springframework.org/schema/beans" id="brokerAuthFilter" class="com.yiwo.plugin.BrokerFilter">
    <property name="jedisPool" ref="jedispool"></property>
  </bean>
</plugins>

 

1.5 重啓測試

正確的用戶名密碼,連接成功

 

錯誤的用戶名密碼,連接失敗

 

2 主題權限

原理和認證相同,可以覆蓋addConsumer實現,這裏不再贅述了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章