SpringBoot

SpringBoot使用Smack實現XMPP訊息推送

蕭智遠 Far Hsiao 2021/11/05 16:59:41
1059

SpringBoot使用Smack實現XMPP訊息推送

 一、前言

XMPP技術簡介

服務端元件Openfire

是基於即時訊息開放協定(XMPP),採用Java語言開發的即時協作伺服器。 Openfire安裝和使用都非常簡單,並利用Web進行管理。 

Java開發庫Smack

開源,易於使用的XMPP用戶端類庫。 

用戶端元件Spark

使用XMPP協議的IM軟體,可以通過Openfire進行文字通訊。

Smack可與不同的Java框架整合使用,本次利用SpringBoot框架實現訊息推送。

二、環境配置

 範例環境

* Java 8

* Spring Boot 2.5.4.RELEASE

* Maven

三、實作步驟

1. 在pom.xml加入smack庫的相關依賴

<!--xmpp  smack-->
<dependency>
    <groupId>org.igniterealtime.smack</groupId>
    <artifactId>smack-core</artifactId>
    <version>4.1.8</version>
</dependency>
<dependency>
    <groupId>org.igniterealtime.smack</groupId>
    <artifactId>smack-tcp</artifactId>
    <version>4.1.8</version>
</dependency>
<dependency>
    <groupId>org.igniterealtime.smack</groupId>
    <artifactId>smack-java7</artifactId>
    <version>4.1.8</version>
</dependency>
<dependency>
    <groupId>org.igniterealtime.smack</groupId>
    <artifactId>smack-extensions</artifactId>
    <version>4.1.8</version>
</dependency>
<dependency>
    <groupId>org.igniterealtime.smack</groupId>
    <artifactId>smack-sasl-provided</artifactId>
    <version>4.1.8</version>
</dependency>

2. 在application.properties加入Openfire伺服器網址(此次使用的是本機)

#openfire伺服器網址
openfire.server=127.0.0.1

3. 代碼實作

package com.example.demosmack;

import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.sasl.provided.SASLPlainMechanism;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
import org.jivesoftware.smackx.search.ReportedData;
import org.jivesoftware.smackx.search.UserSearchManager;
import org.jivesoftware.smackx.xdata.Form;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class SmackController {

    @Value("${openfire.server}")
    protected String openfireServer;

    @RequestMapping("")
    public Object index(String userName, String msg) {
        return sendSmackMessage(userName, msg, false);
    }

    public boolean sendSmackMessage(String userName, String msg, boolean deliveryReceiptRequest) {
        try {
            if (StringUtils.isEmpty(userName)) {
                return false;
            }
            XMPPTCPConnection con = getXmpptcpConnection();
            con.connect();
            if (con.isConnected()) {
                SASLAuthentication.registerSASLMechanism(new SASLPlainMechanism());
                con.loginAnonymously(); //匿名登錄
                String serviceName = con.getServiceName();
                UserSearchManager userSearchManager = new UserSearchManager(con);
                Form searchForm = userSearchManager.getSearchForm("search." + serviceName);
                Form answerForm = searchForm.createAnswerForm();
                answerForm.setAnswer("Username", true);
                answerForm.setAnswer("search", userName);
                ReportedData data = userSearchManager.getSearchResults(answerForm,
                        "search." + serviceName);
                List<ReportedData.Row> list = data.getRows();

                for (ReportedData.Row row : list) {
                    for (String jid : row.getValues("jid")) {
                        Message message = new Message();
                        message.setBody(msg); //發送的訊息
                        message.setTo(jid); //發送的目標
                        if(deliveryReceiptRequest){
                            message.setType(Message.Type.chat); //Type.chat:一對一聊天
                        }else{
                            message.setType(Message.Type.normal); //Type.normal:廣播
                        }
                        message.setSubject(msg);
                        con.sendStanza(message);
                    }
                }
            }
            con.disconnect();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    private XMPPTCPConnection getXmpptcpConnection() {
        XMPPTCPConnectionConfiguration.Builder config=XMPPTCPConnectionConfiguration.builder();
        config.setServiceName(openfireServer);
        config.setHost(openfireServer);
        config.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled);
        config.setSendPresence(true);
        config.setCompressionEnabled(false);

        XMPPTCPConnection con = new XMPPTCPConnection(config.build());
        SASLAuthentication.blacklistSASLMechanism("SCRAM-SHA-1");
        SASLAuthentication.blacklistSASLMechanism("DIGEST-MD5");
        SASLAuthentication.blacklistSASLMechanism("CRAM-MD5");
        return con;
    }
}

代碼解釋:

Controller的sendSmackMessage為推送訊息的方法,userName為對方ID,msg為傳送的訊息,deliveryReceiptRequest是否為離線訊息。

getXmpptcpConnection為連結XMPP的方法。

send方法裡使用的是匿名登錄的方式,也可以用帳號登入成為發送方。

因此發送的訊息都將以廣播的方式推送,離線時接收,在登入Spack後也會收到。

 

4. 測試

啟動專案後,在瀏覽器輸入http://localhost:8080/?userName=test&msg=Smack測試

(已知前提:test為openfire新增的使用者)

在Spack客戶端就可以接收到推送訊息。

四、結語

如專案中有推送訊息的需求,可以考慮XMPP服務作為選擇。

聊天、群組、添加好友等功能,Smack也一應俱全,更多的功能可以查看官方文件

 

參考連結:

Openfire XMPP Smack RTC IM Instant Messaging Chat MD

蕭智遠 Far Hsiao