Apache Vysper 配置参数详解

Apache Vysper 0.7 全部配置文件逐项拆解:spring-config.xml 存储与端点、log4j 日志、ehcache 缓存、JVM 参数与 TLS 证书更换,均在 Ubuntu 22.04 实测验证

Apache VysperApache VysperXMPPJavaUbuntu2026/10/16

Apache Vysper 配置参数详解

目标:把 Apache Vysper 0.7 发行包中的每一个配置文件、每一组参数讲清楚:它控制什么、默认值是什么、改了之后如何验证。本文所有参数修改均在 Ubuntu 22.04 服务器上完成「修改 → 重启 → 实测验证」的完整闭环,关键结论均附真实命令回显。

一、配置体系总览

Vysper 0.7 采用 tar.gz 压缩包发布,解压即用,所有配置集中在安装目录的 config/ 子目录下(find . -maxdepth 2 -type d 实测仅 bin config lib log 四个)。config/ 目录内的文件清单与作用如下(实测 ls -l config/):

-rw-r--r-- 1 root root 5955 Oct 16 07:02 spring-config.xml    # 服务器核心装配(必须)
-rw-r--r-- 1 root root 4351 Oct 16 07:02 log4j.xml             # 日志输出(必须)
-rw-r--r-- 1 root root 9705 Oct 16 07:02 ehcache.xml           # 在线状态缓存(可选)
-rw-r--r-- 1 root root 1847 Oct 16 07:02 bogus_mina_tls.cert   # 自带 TLS 证书(JKS,2005 年生成)

💡 技术拓展:为什么配置目录能被程序读到?bin/run.sh 把安装目录自身与 config/ 一起加入了 JVM classpath(CLASSPATH="$CLASSPATH_PREFIX:$BASEDIR:$BASEDIR/config:$BASEDIR/lib/*")。因此 certificateFile 的 classpath: 前缀实际指向的就是 config/ 目录,这直接解释了第六节的「前导斜杠」坑。

三个业务配置文件(spring-config.xml、log4j.xml、ehcache.xml)的修改都要重启服务才能生效,Vysper 没有运行时热加载。重启的完整命令见《Apache Vysper 常用 CLI 命令与工具详解》。

二、spring-config.xml:服务器核心装配

这是 Vysper 唯一的"主配置",一个 Spring DTD 风格的 XML,负责装配存储注册器、预置账号、网络端点、域名、证书与协议模块。逐段拆解。

2.1 存储注册器:inmemory 与 jcr 两套实现

spring-config.xml 中定义了两个可选的存储注册器 Bean,通过 <alias> 完成二选一:

<bean id="inmemoryStorageRegistry" class="org.apache.vysper.storage.inmemory.MemoryStorageProviderRegistry" />
<bean id="jcrStorageRegistry" class="org.apache.vysper.storage.jcr.JcrStorageProviderRegistry" />
<!-- choose one of the storage registries -->
<alias name="inmemoryStorageRegistry" alias="storageRegistry" />
参数值与含义
inmemoryStorageRegistry纯内存存储,重启后所有账号/花名册/私人数据全部丢失
jcrStorageRegistry基于 Apache Jackrabbit(JCR 内容仓库)的持久存储,落盘 Derby 数据库
alias把选中的 Bean 暴露为 storageRegistry 供其他 Bean 引用,切换存储只改这一行

实测 JCR 持久化闭环。把 alias 指向 jcrStorageRegistry 并重启:

sed -i 's|<alias name="inmemoryStorageRegistry" alias="storageRegistry" />|<alias name="jcrStorageRegistry" alias="storageRegistry" />|' config/spring-config.xml
kill $(ps -ef | awk '/[S]erverMain/{print $2}')
nohup bin/run.sh > /root/vysper-c1-jcr.log 2>&1 & sleep 15
ss -tlnp | grep 5222

实测输出:

LISTEN 0      50                 *:5222            *:*    users:(("java",pid=4431,fd=107))

5222 正常监听,JCR 模式开箱即用。第一次以 JCR 模式启动后,工作目录下多出一个 Jackrabbit 仓库:

ls -d /opt/vysper/vysper-0.7/repository && du -sh /opt/vysper/vysper-0.7/repository
/opt/vysper/vysper-0.7/repository
6.0M    /opt/vysper/vysper-0.7/repository

启动日志中可确认其内嵌 Derby 数据库版本:

18:33:58,568 |-INFO org.apache.jackrabbit.core.persistence.pool.BundleDbPersistenceManager - Database: Apache Derby, Version: 10.2.1.6

持久性验证:JCR 模式下通过管理命令注册 user4(过程见《Apache Vysper 常用 CLI 命令与工具详解》),然后重启服务,再登录 user4:

cd /root && python3 jcr_persist_check.py

实测输出:

USER4-LOGIN-RESULT: SUCCESS

结论:JCR 模式下通过 XEP-0133 动态注册的账号跨重启存活,而 in-memory 模式下同一操作重启后登录返回 <failure><not-authorized/></failure>(对照实测)。生产上想保住账号,就把 alias 切到 jcrStorageRegistry,或者用 jcr 的同时删掉 2.2 节的预置账号 Bean。

💡 技术拓展:JCR(Java Content Repository,JSR-170/283)是一种面向树形内容的 Java 内容仓库 API,Jackrabbit 是其参考实现。Vysper 用它持久化账号、花名册、私人存储(XEP-0049 Private Data)等,仓库数据全部落在 repository/ 目录,备份该目录即备份了用户数据。

2.2 预置账号:addUsers Bean

<bean id="addUsers" class="org.apache.vysper.spring.AddUserHelper">
    <constructor-arg index="0">
        <map>
            <entry key="admin@vysper.org" value="CHOOSE A SECURE PASSWORD" />
            <entry key="user1@vysper.org" value="CHOOSE A SECURE PASSWORD" />
        </map>
    </constructor-arg>
    <property name="storageProviderRegistry" ref="storageRegistry" />
</bean>

参数说明:

位置含义
key="admin@vysper.org"JID(节点名@服务域名),域名必须与 2.5 节的 constructor-arg 一致,否则该账号无法登录
value="..."明文密码。官方注释特别说明:密码留空会生成随机密码并打印在控制台,默认安装是安全的
<property name="storageProviderRegistry" ...>把账号注册进当前激活的存储(别名 storageRegistry 指向谁就写进谁)

官方注释同时提醒:remove if you have persistently stored users——如果已经用持久存储并靠管理命令/带内注册维护账号,应删除本 Bean,避免每次启动都重建固定的演示账号。

实测修改方式(把默认口令替换为测试口令并新增 user1/user2):

cp config/spring-config.xml config/spring-config.xml.orig
sed -i 's|<entry key="admin@vysper.org" value="CHOOSE A SECURE PASSWORD" />|<entry key="admin@vysper.org" value="q1w2e3r4" />\n                <entry key="user1@vysper.org" value="q1w2e3r4" />\n                <entry key="user2@vysper.org" value="q1w2e3r4" />|' config/spring-config.xml
sed -n '35,44p' config/spring-config.xml

实测输出:

        <constructor-arg index="0">
            <map>
                <entry key="admin@vysper.org" value="q1w2e3r4" />
                <entry key="user1@vysper.org" value="q1w2e3r4" />
                <entry key="user2@vysper.org" value="q1w2e3r4" />
                <!-- 其余为发行包自带的注释占位项,略 -->
            </map>
        </constructor-arg>

重启后用客户端脚本以 user1/user2 登录并互发消息(脚本见《Ubuntu 22.04 安装 Apache Vysper 指南》安装验证一节),实测输出:

--- [user1] TLS established: TLSv1.2
=== [user1] login OK, full jid = user1@vysper.org/051a8713c60e4704b1c968f2d4d57d0e
=== [user2] login OK, full jid = user2@vysper.org/386c8ad3baf942ca8e3951a6b36247c9
ALL MESSAGE EXCHANGE OK

两个 map 内账号均可 SASL 认证通过,证明每一条 <entry> 都会在启动时写入当前存储。

💡 技术拓展:预置账号只是演示便利,Vysper 支持三种账号维护方式:① 本节的 addUsers 静态预置;② XEP-0133 服务管理命令在线加账号(需 admin 权限,实测见《Apache Vysper 常用 CLI 命令与工具详解》);③ XEP-0077 带内注册由用户自助注册(见 2.6 节实测)。三者账号最终都落在当前存储注册器里。

2.3 TCP 端点:TCPEndpoint

<bean id="tcpEndpoint" class="org.apache.vysper.mina.TCPEndpoint">
</bean>

默认属性全部留空:使用默认端口 5222、TLS 证书取 server Bean 中配置的那份。TCPEndpoint 支持哪些属性,直接看字节码(javadoc 与发行包文档均不完整):

/opt/jdk/jdk8u504-b01/bin/javap -classpath lib/vysper-core-0.7.jar org.apache.vysper.mina.TCPEndpoint

实测输出:

public class org.apache.vysper.mina.TCPEndpoint extends org.apache.vysper.xmpp.net.Endpoint {
  public org.apache.vysper.mina.TCPEndpoint();
  public void setPort(int);
  public void setSSLEnabled(boolean);
  public void setSSLCertificateFile(java.lang.File);
  public void setSSLCertificatePassphrase(java.lang.String);
}
属性类型说明
portintTCP 监听端口,默认 5222
sslEnabledboolean是否在端点级启用 TLS(默认继承 server Bean 的证书配置)
sslCertificateFileFile端点专属证书文件
sslCertificatePassphraseString端点专属证书口令

实测改端口:在 Bean 内加一行 <property name="port" value="15222"/>,重启后验证:

sed -i 's|<bean id="tcpEndpoint" class="org.apache.vysper.mina.TCPEndpoint">|<bean id="tcpEndpoint" class="org.apache.vysper.mina.TCPEndpoint">\n        <property name="port" value="15222"/>|' config/spring-config.xml
kill $(ps -ef | awk '/[S]erverMain/{print $2}')
nohup bin/run.sh > /root/vysper-c1-port.log 2>&1 & sleep 12
ss -tlnp | grep -E "15222|5222"

实测输出:

LISTEN 0      50                 *:15222           *:*    users:(("java",pid=3973,fd=107))

5222 不再监听、15222 生效。客户端脚本把目标端口改成 15222 后重跑,登录与消息互发全部成功:

--- [user1] TLS established: TLSv1.2
=== [user1] login OK, full jid = user1@vysper.org/...
=== [user2] login OK, full jid = user2@vysper.org/...
ALL MESSAGE EXCHANGE OK

验证后删除该行还原 5222。改端口一般用于 5222 被占用或想规避运营商封禁的场景。

💡 技术拓展:XMPP 官方约定 5222 为客户端到服务器(c2s)端口、5269 为服务器间联帮(s2s)端口。Vysper 0.7 没有实现 s2s(服务器到服务器)联帮,所以这里只关心 5222。

2.4 BOSH 端点:BoshEndpoint

发行包默认把 BOSH 端点注释掉。取消注释即可启用 HTTP 传输(XEP-0124/0206):

<bean id="boshEndpoint" class="org.apache.vysper.xmpp.extension.xep0124.BoshEndpoint">
    <property name="accessControlAllowOrigin">
        <list><value>*</value></list>
    </property>
    <property name="port" value="8080" />
    <property name="contextPath" value="/bosh" />
</bean>

并在 server Bean 的 endpoints 列表中加 <ref bean="boshEndpoint"/>。逐参数说明:

参数默认值含义
port无(必须显式配置)HTTP 监听端口,示例用 8080
contextPath无(必须显式配置)HTTP 上下文路径,客户端 POST 地址为 http://IP:PORT/contextPath
accessControlAllowOrigin无BOSH 响应的 CORS 头 Access-Control-Allow-Origin 列表,浏览器端 Web 客户端跨域调用必须配,* 表示全部放行

BoshEndpoint 完整属性(javap 实测,除 setContextPath/setAccessControlAllowOrigin/setPort 外还有三组 SSL 属性):

public void setSSLEnabled(boolean);
public void setSSLCertificateKeystore(java.lang.String);
public void setSSLCertificateKeystorePassword(java.lang.String);

后三个属性可在 HTTP 层配置独立证书与口令(HTTPS BOSH)。setSSLEnabled(true) 且不单独设置 keystore 时,会退回使用内嵌 Jetty 的默认行为——0.7 代码较老,此组参数建议以"能配"为预期,若需 HTTPS 建议前置 Nginx 反代终结 TLS,而不是依赖这个 2011 年的 Jetty。

实测启用:取消注释并重启后:

ss -tlnp | grep -E "5222|8080"
LISTEN 0      50                 *:8080            *:*    users:(("java",pid=5676,fd=119))
LISTEN 0      50                 *:5222            *:*    users:(("java",pid=5676,fd=113))

用 curl 模拟 BOSH 客户端创建会话(注意 contextPath 有尾斜杠,访问 /bosh 会 302 到 /bosh/):

curl -s -i -X POST -H 'Content-Type: text/xml; charset=utf-8' \
  --data "<body content='text/xml; charset=utf-8' hold='1' rid='100000001' to='vysper.org' ver='1.9' wait='60' xml:lang='en' xmlns='http://jabber.org/protocol/httpbind'/>" \
  http://127.0.0.1:8080/bosh/ | head -12

实测输出(会话创建成功,拿到 sid):

HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Access-Control-Allow-Origin: *
Server: Vysper/0.5

<body wait='60' xmlns:stream='http://etherx.jabber.org/streams' inactivity='10' polling='5' ack='1' sid='fe93e78c-4ce5-4b3f-a52f-ec0a99b601e2' ver='1.9' requests='2' hold='1' secure='false' from='vysper.org' authid='fe93e78c-4ce5-4b3f-a52f-ec0a99b601e2' maxpause='120' xmlns='http://jabber.org/protocol/httpbind'/>

会话创建成功(拿到 sid)。注意 Server: Vysper/0.5——0.7 发行包的 BOSH 响应头仍写着 0.5,属于历史遗留的版本串,不代表运行版本,可通过 2.6 节的 SoftwareVersionModule(返回 0.7)交叉验证。

💡 技术拓展:BOSH(Bidirectional-streams Over Synchronous HTTP)让 XMPP 跑在 HTTP 长轮询上,是 WebSocket 之前浏览器端即时通讯的主流方案。rid 是请求序号、sid 是会话标识、hold 是服务端挂起请求数,这几个参数是 BOSH 协议的握手核心。

2.5 server Bean:域名、证书与模块装配

server Bean 是整个 Spring 装配的终点,逐行拆解:

<bean id="server" class="org.apache.vysper.spring.SpringCompatibleXMPPServer"
      init-method="init" destroy-method="destroy">
    <!-- TODO change domain name to your domain -->
    <constructor-arg value="vysper.org"/>
    <property name="endpoints">
        <list>
            <ref bean="tcpEndpoint"/>
            <ref bean="boshEndpoint"/>
        </list>
    </property>
    <property name="storageProviderRegistry" ref="storageRegistry" />
    <property name="certificateFile" value="classpath:bogus_mina_tls.cert" />
    <property name="certificatePassword" value="boguspw" />
    <property name="modules" ><list> ... </list></property>
</bean>
参数说明
<constructor-arg value="vysper.org"/>服务域名,决定流头 from=、账号 JID 后缀、disco 身份。改名必须同步改 2.2 节所有账号 key 与 2.4 节 BOSH 的 to 属性
endpoints启用的端点列表,TCP 必选,BOSH 可选
storageProviderRegistry固定引用别名 storageRegistry(即 2.1 节二选一的结果)
certificateFileTLS 证书文件(JKS keystore 格式),默认用内置 2005 年的 bogus 证书
certificatePassword证书 keystore 口令,与 bogus_mina_tls.cert 配套为 boguspw
modules启用的协议模块清单

域名实测:把 vysper.org 改为 im.demo.local 并重启:

sed -i 's|<constructor-arg value="vysper.org"/>|<constructor-arg value="im.demo.local"/>|' config/spring-config.xml
kill $(ps -ef | awk '/[S]erverMain/{print $2}')
nohup bin/run.sh > /root/vysper-domain2.log 2>&1 &
sleep 12

裸流探测确认服务器身份已变(单行脚本,实测输出见下):

python3 -c "
import socket
s = socket.create_connection(('127.0.0.1', 5222), 10)
s.sendall(b\"<?xml version='1.0'?><stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' to='im.demo.local' version='1.0'>\")
print(s.recv(4096).decode()[:300]); s.close()"

实测输出:

<?xml version="1.0" encoding="UTF-8"?><stream:stream xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" from="im.demo.local" version="1.0" id="7f3ca97451c546a3a86ee8f7ba8a1ef7">

流头 from="im.demo.local" 生效。注意此时旧账号 user1@vysper.org 用的 SASL 认证 JID 与新域名不匹配会登录失败——改域名必须同步改账号,这是本参数最大的联动项。

modules 清单与实测状态(当前发行包内 jar 均在 classpath,取消注释即启用):

模块 BeanXEP功能本文实测状态
SoftwareVersionModuleXEP-0092软件版本应答启用可用
EntityTimeModuleXEP-0202/0203实体时间启用可用
VcardTempModuleXEP-0054用户名片启用可用
XmppPingModuleXEP-0119XMPP Ping启用可用
PrivateDataModuleXEP-0049私人数据存储启用可用
AdhocCommandsModuleXEP-0050Ad-hoc 命令框架启用可用
ServiceAdministrationModuleXEP-0133服务管理命令(加用户等)启用可用,addAdmins 列表指定管理员 JID
MUCModuleXEP-0045多人聊天室启用可用
PublishSubscribeModuleXEP-0060发布订阅发行包已含 jar,默认注释
InBandRegistrationModuleXEP-0077带内注册默认未启用,见 2.6 实测

addAdmins 是 ServiceAdministrationModule 的属性,决定谁可以执行 XEP-0133 管理命令,值必须是完整 JID 且与登录账号一致:

<bean class="org.apache.vysper.xmpp.modules.extension.xep0133_service_administration.ServiceAdministrationModule" >
    <property name="addAdmins">
        <list><value>admin@vysper.org</value></list>
    </property>
</bean>

2.6 启用 XEP-0077 带内注册(模块开关实测)

在 modules 列表加一行 Bean 即可启用带内注册:

<bean class="org.apache.vysper.xmpp.modules.extension.xep0077_inbandreg.InBandRegistrationModule"/>

重启后实测完整闭环(先 admin 上线,再注册 user5 并向 admin 发消息):

<<< [reg] <iq xmlns="jabber:client" type="result" id="r1" from="vysper.org"><query xmlns="jabber:iq:register"><registered/><username>user5</username></query></iq>
=== [user5] login OK
=== USER5 -> ADMIN MESSAGE OK (XEP-0077 on Spring runtime)

注册应答 type="result" 且带 <registered/> 标记,新账号立即登录成功。启用前需评估:0.7 的带内注册没有验证码/邮件确认等防滥用机制,暴露公网即等于开放注册,生产环境建议关闭或只在内网启用。

三、log4j.xml:日志参数

Vysper 用 log4j 1.x,配置在 config/log4j.xml,定义三个 appender:

Appender类型输出目标Threshold 默认值
CONSConsoleAppenderSystem.out(即 nohup 重定向的文件)TRACE
FILERollingFileAppenderlog/vysper_server.logINFO
STANZARollingFileAppenderlog/stanza.logINFO

三个 appender 的共同参数:

参数说明
File日志文件路径,相对 run.sh 的启动目录
Append追加模式,重启不清空
MaxFileSize单文件上限 500KB,超过滚动
MaxBackupIndex保留 1 个滚动备份,即最多 vysper_server.log + vysper_server.log.1 两个文件
ConversionPattern输出格式:`时间

分类别调优(category 节点):

<category name="org.apache.mina"><priority value="WARN"/></category>
<category name="org.apache.vysper"><priority value="DEBUG"/></category>
<category name="stanza"><priority value="INFO"/><appender-ref ref="STANZA"/></category>

stanza 类别专收 XMPP 报文:stanza.log 一行一条记录收发的每一个 stanza(方向、类型、内容摘要),排查协议问题时价值最高。实测样式:

INFO  07:25:58,263 | client | iq.id='d1'.to='vysper.org'.type='get'.query.disco.info
INFO  07:25:58,264 | server | iq.id='d1'.from='vysper.org'.type='result'.query.disco.info.feature='http://jabber.org/protocol/disco#info'.feature='jabber:iq:version'.feature='urn:xmpp:time'.feature='vcard-temp'.feature='urn:xmpp:ping'
INFO  07:25:58,265 | client | message.body.hello from user1 at 2026-10-16 07:25:58
INFO  07:25:58,304 | server | message.body.reply from user2

其中 client 前缀代表客户端发来的报文、server 前缀代表服务器回出的报文。

实测 Threshold 调优:把 CONS 的 Threshold 从 TRACE 改为 WARN,观察控制台输出量变化:

sed -i '0,/<param name="Threshold" value="TRACE"\/>/s//<param name="Threshold" value="WARN"\/>/' config/log4j.xml
kill $(ps -ef | awk '/[S]erverMain/{print $2}') 2>/dev/null; sleep 3
cd /opt/vysper/vysper-0.7 && nohup bin/run.sh > /root/vysper-log4j-warn.log 2>&1 &
sleep 12
wc -l < /root/vysper-log4j-warn.log

实测输出:

5

同样 12 秒窗口,TRACE 阈值下控制台日志超过 60 行(模块加载、存储注册、端点监听等全量 INFO),WARN 阈值下仅剩 5 行(只剩真正的 WARN/ERROR)。生产后台运行建议 WARN;排障期改回 TRACE 并重启。注意改 CONS 不影响 FILE/STANZA,log/ 下两个文件始终按自身阈值记录。

四、ehcache.xml:在线状态缓存

config/ehcache.xml 供 EhCachePresenceCacheImpl 使用——该类从 classpath 加载此文件构建缓存(源码注释明确:缓存策略 LRU,参数可通过 ehcache.xml 调整)。默认只有两个 cache 定义:

<cache name="PresenceCache"
       maxElementsInMemory="10000"
       eternal="true"
       overflowToDisk="false"
       memoryStoreEvictionPolicy="LRU"/>

<cache name="JIDCache"
       maxElementsInMemory="10000"
       eternal="true"
       overflowToDisk="false"
       memoryStoreEvictionPolicy="LRU"/>
参数值含义
maxElementsInMemory10000内存中最多缓存的元素数(在线状态条目/JID 条目)
eternaltrue条目永不过期(在线状态有自己的生命周期,不走 TTL)
overflowToDiskfalse不落盘,纯内存
memoryStoreEvictionPolicyLRU超过上限时淘汰最久未使用条目

大规模式部署时按并发在线用户数上调 maxElementsInMemory 即可;该文件缺失或非法时缓存类会回退默认行为,但日志会出现 WARN。

五、JVM 参数:bin/setenv.sh

run.sh 启动时会 source 同目录 setenv.sh(默认不存在,需自建)。这是配置 JAVA_HOME 与 JVM 内存的正规入口:

cat > bin/setenv.sh <<'EOF'
export JAVA_HOME=/opt/jdk/jdk8u504-b01
export JAVA_OPTS="-Xms256m -Xmx256m"
EOF
chmod +x bin/setenv.sh

参数逐项说明:

变量说明
JAVA_HOME指定 JDK。Vysper 0.7 是 2011 年代码,实测 Temurin 8(1.8.0_504-b01)可用;更高版本 JDK 未通过验证(Spring 2.x 字节码增强与老依赖存在兼容风险)
JAVA_OPTS传给 JVM 的参数。-Xms/-Xmx 设堆大小,100 并发内 256m 足够
CLASSPATH_PREFIXrun.sh 支持的高级变量:需要外加 jar(如自研模块)时把目录或 jar 路径赋给它

启动后用 jps 验证参数确实生效:

/opt/jdk/jdk8u504-b01/bin/jps -v | grep ServerMain

实测输出:

3948 ServerMain -Xms256m -Xmx256m

💡 技术拓展:若 JAVA_OPTS 写成老式 -XX:PermSize=64m -XX:MaxPermSize=256m,JDK 8 启动日志会输出 Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize...; support was removed in 8.0——参数被忽略但不影响启动(持久代元空间化)。该警告在 JDK 7 下不会出现。日志格式详见实测记录,属无害警告,建议直接删掉 PermSize 参数。

六、TLS 证书:更换自带 2005 年证书全流程

默认证书 bogus_mina_tls.cert 是 2005 年生成的 511 位 MD5 签名 JKS(口令 boguspw),现代客户端对其约束很严:OpenSSL 3.0 默认安全级别直接拒绝(ca md too weak / key too small),JDK 8 客户端报 Certificates do not conform to algorithm constraints,python 也必须在握手时强制 TLS 1.2 才能连上(详见《Ubuntu 22.04 安装 Apache Vysper 指南》的兼容性说明)。生产环境必须换证,完整流程实测如下。

6.1 生成新证书

/opt/jdk/jdk8u504-b01/bin/keytool -genkeypair -alias vysper -keyalg RSA -keysize 2048 \
  -sigalg SHA256withRSA -validity 3650 -keystore config/vysper_tls.jks \
  -storepass changeme -keypass changeme \
  -dname "CN=vysper.org, OU=Demo, O=Demo, L=Beijing, C=CN"

命令无报错即生成成功,config/vysper_tls.jks 约 2.2KB。Vysper 的 certificateFile 实际加载的是 JKS keystore(内置证书也是 JKS),keytool 默认格式即 JKS,直接可用。

6.2 配置与「前导斜杠」坑

sed -i 's|<property name="certificateFile" value="classpath:bogus_mina_tls.cert" />|<property name="certificateFile" value="/opt/vysper/vysper-0.7/config/vysper_tls.jks" />|; s|<property name="certificatePassword" value="boguspw" />|<property name="certificatePassword" value="changeme" />|' config/spring-config.xml

重启后 5222 不监听、进程在,日志报错(-A3 只截关键三行):

Caused by: org.springframework.beans.factory.BeanDefinitionStoreException:
class path resource [opt/vysper/vysper-0.7/config/vysper_tls.jks] cannot be opened because it does not exist

原因:certificateFile 的值按 Spring 资源路径解析,绝对路径的前导 / 被剥离,当成 classpath 相对路径去找。正确写法是加 file: 前缀:

<property name="certificateFile" value="file:/opt/vysper/vysper-0.7/config/vysper_tls.jks" />
<property name="certificatePassword" value="changeme" />

6.3 验证:openssl、python、Java 三方回归

重启后先用 openssl 确认新证书生效(这次不降级、不加任何放宽参数):

echo | openssl s_client -starttls xmpp -connect 127.0.0.1:5222 -servername vysper.org 2>/dev/null | openssl x509 -noout -subject -dates

实测输出:

subject=CN=vysper.org, OU=Demo, O=Demo, L=Beijing, C=CN
notBefore=Oct 16 07:04:02 2026 GMT
notAfter=Oct 13 07:04:02 2036 GMT

再看协商结果:

New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Verification error: self-signed certificate

(命令同上:echo | openssl s_client -starttls xmpp -connect 127.0.0.1:5222 2>/dev/null | grep -E "New, |Verification"。)老证书下 openssl 协商 TLS 1.2 且报 ca md too weak;换新证书后 TLS 1.3 直接协商成功。Verification error: self-signed certificate 是自签证书的固有提示,客户端侧导入信任或跳过校验即可,不影响服务端配置正确性。

python 客户端(TLS 1.2)与 Java 客户端回归登录+消息互发,实测尾部回显:

ALL MESSAGE EXCHANGE OK
ALL OK: legacy-TLS login + message exchange verified

验证完成后如需还原默认证书,把两行改回 classpath:bogus_mina_tls.cert / boguspw 重启即可。

💡 技术拓展:正式环境建议用 Let's Encrypt 证书(PEM+Key),但 Vysper 只认 JKS,需先用 openssl 把 PEM 转 PKCS12、再用 keytool 导入 JKS;或者更省事——5222 后面挂 Nginx/Haproxy 做 TLS 终结,Vysper 端点只对内网监听。

七、常见问题与排查

1. 改完配置 5222 不监听,但进程还在? 先看启动日志(nohup 重定向的文件)grep -A3 Caused。本机实测两种典型:证书路径前导斜杠坑(见 6.2);端口被上一实例占用——此时 Vysper 绑定失败但不会退出,进程变成"僵尸",先 ps -ef | grep ServerMain 清理所有旧实例再启动:

Caused by: org.apache.mina.core.RuntimeIoException: Failed to bind
... Address already in use

2. BOSH 响应头为什么写 Server: Vysper/0.5? 0.7 发行包里 BOSH 模块的版本串没更新,历史遗留,与实际运行版本无关。核实真实版本用 XEP-0092:<iq type='get' id='v1'><query xmlns='jabber:iq:version'/></iq> 应答 <version>0.7</version>。

3. XEP-0133 加用户的表单字段名是什么? 必须用 accountjid / password / password-verify(单数、jid-single),不是较新 XEP-0133 修订里的 accountjids 复数形式,字段名错了服务器返回 action-not-allowed。

4. 改了域名后客户端登录失败? SASL PLAIN 认证的 authzid/authcid 里 JID 后缀必须与新域名一致,预置账号的 key 也要同步改,否则 <failure><not-authorized/></failure>。

5. JCR 模式报 Jackrabbit 初始化错误? 确认 lib/ 下 jackrabbit-*、derby-*.jar 在 classpath(发行包默认齐全),且启动目录可写(会生成 repository/)。Vysper 没提供仓库路径参数,仓库位置固定为启动时的工作目录,因此用 systemd 托管时务必固定 WorkingDirectory。


本文参数均在 Ubuntu 22.04 + Vysper 0.7 + Temurin JDK 8(1.8.0_504-b01)环境实测,交叉验证可复现。安装与闭环验证见《Ubuntu 22.04 安装 Apache Vysper 指南》,命令行工具与运行观测见《Apache Vysper 常用 CLI 命令与工具详解》。