Java使用ssl证书认证连接mqtt服务

项目架构

pom

   <dependency>
            <groupId>org.eclipse.paho</groupId>
            <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
            <version>1.2.0</version>
   </dependency>

   <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcpkix-jdk15on</artifactId>
            <version>1.47</version>
   </dependency>

客户端

@Slf4j
public class MQTTPublishClient {

    /**
     * tcp://MQTT安装的服务器地址:MQTT定义的端口号
     */
    public String HOST;

    /**
     * 定义MQTT的ID,可以在MQTT服务配置中指定
     */
    private String clientId;

    private MqttClient client;

    @Value("${jsjc.switch.open-mqtt}")
    private Boolean openMqtt = true;

    @Setter
    @Getter
    private MqttTopic mqttTopic;

    /**
     * 账号信息
     */
    private String userName = "***";

    /**
     * 密码
     */
    private String password = "****";


    /**
     * 构造函数
     *
     * @throws MqttException
     */
    public MQTTPublishClient(String host, String serverId) throws MqttException {

        if (openMqtt) {
            HOST = host;
            clientId = serverId;
            try {
                if (client == null) {
                    client = new MqttClient(HOST, clientId, new MemoryPersistence());
                }
            } catch (MqttException e) {
                e.printStackTrace();
            }
            connect();

            
            String[] topic1 = {"", "", "", "", "", ""};

            int[] qos = {0, 0, 0, 0, 0, 0};
            //订阅主题 
            client.subscribe(topic1, qos);
        }
    }

    /**
     * 用来连接服务器
     */
    private void connect() {
        MqttConnectOptions options = new MqttConnectOptions();
        options.setCleanSession(false);
        // 设置超时时间
        options.setConnectionTimeout(300);
        // 设置会话心跳时间
        options.setKeepAliveInterval(300);
        //设置自动重连
        options.setAutomaticReconnect(true);
        options.setUserName(userName);
        options.setPassword(password.toCharArray());
        try {
            options.setSocketFactory(SslUtil.getSocketFactory("./root.pem", "./client.pem", "./client.key", ""));
            client.setCallback(new PublishCallback());
            client.connect(options);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //发送消息并获取回执
    public void publish(String topic, MqttMessage message) throws MqttPersistenceException, MqttException, InterruptedException {
        mqttTopic = client.getTopic(topic);
        MqttDeliveryToken token = mqttTopic.publish(message);
        token.waitForCompletion();
        token.getResponse();
    }
}

订阅回调函数

@Slf4j
public class PublishCallback implements MqttCallbackExtended {

    @Override
    public void connectionLost(Throwable cause) {
        // 连接丢失后,一般在这里面进行重连
        log.info("[PublishCallback] 连接断开");
    }

    @Override
    public void deliveryComplete(IMqttDeliveryToken token) {
        log.info("deliveryComplete---------" + token.isComplete());
    }

    @Override
    public void messageArrived(String topic, MqttMessage message) throws Exception {
        try {
            //业务层处理逻辑,处理订阅主题的消息体
            MyService ervice = SpringUtil.getBean(MyService.class);

            log.info("接收消息主题 : " + topic);
            String str = message.toString();
            log.info("从MQTT收到的消息为:" + str + " ;TOPIC:" + topic);
        } catch (Exception e) {
            log.error("PublishCallback error:" + e.getMessage());
            e.printStackTrace();
        }
    }

    @Override
    public void connectComplete(boolean b, String s) {
        log.info("connected");
    }
}

tip 

回调函数中引入业务层类,自动注解@Autowired  会使Spring容器无法加载,需手动引入bean

            MyService service = SpringUtil.getBean(MyService.class);

工具类

SslUtil

public class SslUtil {

    public static SSLSocketFactory getSocketFactory(final String caCrtFile, final String crtFile, final String keyFile,
                                                    final String password) throws Exception {
        Security.addProvider(new BouncyCastleProvider());

        // load CA certificate
        PEMReader reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(caCrtFile)))));
        X509Certificate caCert = (X509Certificate) reader.readObject();
        reader.close();

        // load client certificate
        reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(crtFile)))));
        X509Certificate cert = (X509Certificate) reader.readObject();
        reader.close();

        // load client private key
        reader = new PEMReader(
                new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(keyFile)))),
                new PasswordFinder() {
                    @Override
                    public char[] getPassword() {
                        return password.toCharArray();
                    }
                }
        );
        KeyPair key = (KeyPair) reader.readObject();
        reader.close();

        // CA certificate is used to authenticate server
        KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());
        caKs.load(null, null);
        caKs.setCertificateEntry("ca-certificate", caCert);
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(caKs);

        // client key and certificates are sent to server so it can authenticate us
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(null, null);
        ks.setCertificateEntry("certificate", cert);
        ks.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(), new java.security.cert.Certificate[]{cert});
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(ks, password.toCharArray());

        // finally, create SSL socket factory
        SSLContext context = SSLContext.getInstance("TLSv1.2");
        context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

        return context.getSocketFactory();
    }
}

SpringUtil

@Component
@Slf4j
public class SpringUtil implements ApplicationContextAware {


    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (SpringUtil.applicationContext == null) {
            SpringUtil.applicationContext = applicationContext;
        }
    }

    /**
     * 获取applicationContext
     *
     * @return
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 通过name获取 Bean
     *
     * @param name
     * @return
     */
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }

    /**
     * 通过class获取Bean
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> clazz) {
        T bean = getApplicationContext().getBean(clazz);
        return bean;
    }

    /**
     * 通过name,以及Clazz返回指定的Bean
     * @param name
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(String name, Class<T> clazz) {
        return getApplicationContext().getBean(name, clazz);
    }

}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值